Skip to main content

pharos_native/runtime/
generated.rs

1use std::fs;
2use std::path::Path;
3
4use serde_json::{Map, Value};
5
6use crate::config::AppRuntime;
7
8use super::business::tenant_id_for_business_base;
9use super::dynamic::generated_surface_label;
10
11#[derive(Debug, Clone, Default, Eq, PartialEq)]
12/// Public struct `GeneratedSurfaceApiMatchRuntime` used by this module.
13///
14/// # Fields
15///
16/// - `fixture_root`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
17/// - `state_root`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
18/// - `form_root`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
19/// - `surface_root`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
20/// - `context_root`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
21/// - `surface_id`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
22/// - `operation_name`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
23/// - `business_slug`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
24/// - `fixture_path`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
25/// - `form_fixture_path`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
26/// - `context_id`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
27/// - `tenant_id`: Stored field on `GeneratedSurfaceApiMatchRuntime`.
28///
29/// # Examples
30///
31/// ```rust,ignore
32/// let _ = std::mem::size_of::<pharos_native::runtime::generated::GeneratedSurfaceApiMatchRuntime>();
33/// ```
34///
35/// # Related
36///
37/// - [`crate::runtime::generated`] for constructors, helpers, and neighboring runtime types.
38pub struct GeneratedSurfaceApiMatchRuntime {
39    pub fixture_root: Value,
40    pub state_root: Value,
41    pub form_root: Option<Value>,
42    pub surface_root: Value,
43    pub context_root: Value,
44    pub surface_id: Option<String>,
45    pub operation_name: Option<String>,
46    pub business_slug: Option<String>,
47    pub fixture_path: Option<String>,
48    pub form_fixture_path: Option<String>,
49    pub context_id: i64,
50    pub tenant_id: i64,
51}
52
53/// Returns the JSON long.
54fn json_long(value: &Value, key: &str, default_value: i64) -> i64 {
55    value
56        .get(key)
57        .and_then(Value::as_i64)
58        .unwrap_or(default_value)
59}
60
61/// Reads JSON file.
62fn read_json_file(path: &Path) -> Option<Value> {
63    serde_json::from_str(&fs::read_to_string(path).ok()?).ok()
64}
65
66/// Performs truthy text.
67///
68/// # Parameters
69///
70/// - `text`: Input consumed by [`crate::runtime::generated::truthy_text`].
71///
72/// # Returns
73///
74/// Returns `true` when the condition checked by [`crate::runtime::generated::truthy_text`] holds and `false` otherwise.
75///
76/// # Examples
77///
78/// ```rust,ignore
79/// let _result = pharos_native::runtime::generated::truthy_text("{}");
80/// ```
81///
82/// # Related
83///
84/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
85pub fn truthy_text(text: &str) -> bool {
86    matches!(
87        text.trim().to_ascii_lowercase().as_str(),
88        "true" | "1" | "yes" | "on"
89    )
90}
91
92/// Performs digits only text.
93fn digits_only_text(text: &str) -> bool {
94    !text.is_empty() && text.chars().all(|ch| ch.is_ascii_digit())
95}
96
97/// Performs value to string.
98fn value_to_string(value: &Value) -> String {
99    match value {
100        Value::Null => String::new(),
101        Value::String(text) => text.clone(),
102        Value::Bool(flag) => flag.to_string(),
103        Value::Number(number) => number.to_string(),
104        _ => serde_json::to_string(value).unwrap_or_default(),
105    }
106}
107
108/// Performs fixture path.
109///
110/// # Parameters
111///
112/// - `app`: Input consumed by [`crate::runtime::generated::fixture_path`].
113/// - `relative_path`: Input consumed by [`crate::runtime::generated::fixture_path`].
114///
115/// # Returns
116///
117/// Returns [`Some`] when [`crate::runtime::generated::fixture_path`] can produce a value and [`None`] otherwise.
118///
119/// # Performance
120///
121/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::fixture_path`].
122///
123/// # Examples
124///
125/// ```rust,ignore
126/// let _result = pharos_native::runtime::generated::fixture_path(todo!(), "page");
127/// ```
128///
129/// # Related
130///
131/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
132pub fn fixture_path(app: &AppRuntime, relative_path: &str) -> Option<String> {
133    if relative_path.is_empty() {
134        return None;
135    }
136    for base in [app.artifact_dir.as_deref(), app.app_root.as_deref()] {
137        let Some(base) = base.filter(|value| !value.is_empty()) else {
138            continue;
139        };
140        let candidate = Path::new(base).join(relative_path);
141        if candidate.exists() {
142            return Some(candidate.to_string_lossy().into_owned());
143        }
144    }
145    None
146}
147
148/// Performs generated surface fixture path.
149///
150/// # Parameters
151///
152/// - `app`: Input consumed by [`crate::runtime::generated::generated_surface_fixture_path`].
153///
154/// # Returns
155///
156/// Returns [`Some`] when [`crate::runtime::generated::generated_surface_fixture_path`] can produce a value and [`None`] otherwise.
157///
158/// # Examples
159///
160/// ```rust,ignore
161/// let _result = pharos_native::runtime::generated::generated_surface_fixture_path(todo!());
162/// ```
163///
164/// # Related
165///
166/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
167pub fn generated_surface_fixture_path(app: &AppRuntime) -> Option<String> {
168    fixture_path(app, "data/contracts/generated-surfaces.json")
169}
170
171/// Performs generated manage path segment.
172fn generated_manage_path_segment(manage_path: &str) -> Option<String> {
173    manage_path
174        .trim_matches('/')
175        .split('/')
176        .filter(|segment| !segment.is_empty())
177        .rev()
178        .find(|segment| !segment.starts_with('{') && !segment.starts_with(':'))
179        .map(ToOwned::to_owned)
180}
181
182/// Builds the runtime generated admin surface find.
183///
184/// # Parameters
185///
186/// - `fixture_root`: Input consumed by [`crate::runtime::generated::runtime_generated_admin_surface_find`].
187/// - `surface_kind`: Input consumed by [`crate::runtime::generated::runtime_generated_admin_surface_find`].
188///
189/// # Returns
190///
191/// Returns [`Some`] when [`crate::runtime::generated::runtime_generated_admin_surface_find`] can produce a value and [`None`] otherwise.
192///
193/// # Performance
194///
195/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::runtime_generated_admin_surface_find`].
196///
197/// # Examples
198///
199/// ```rust,ignore
200/// let _result = pharos_native::runtime::generated::runtime_generated_admin_surface_find(todo!(), todo!());
201/// ```
202///
203/// # Related
204///
205/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
206pub fn runtime_generated_admin_surface_find<'a>(
207    fixture_root: &'a Value,
208    surface_kind: &str,
209) -> Option<&'a Value> {
210    if surface_kind.is_empty() {
211        return None;
212    }
213    fixture_root
214        .get("surfaces")
215        .and_then(Value::as_array)?
216        .iter()
217        .find(|surface| {
218            if surface.get("surface_kind").and_then(Value::as_str) != Some("generated_admin") {
219                return false;
220            }
221            let manage_path = surface
222                .get("operations")
223                .and_then(|value| value.get("manage"))
224                .and_then(|value| value.get("path"))
225                .and_then(Value::as_str);
226            manage_path
227                .and_then(generated_manage_path_segment)
228                .as_deref()
229                == Some(surface_kind)
230        })
231}
232
233/// Performs dynamic dispatch extract param.
234///
235/// # Parameters
236///
237/// - `route_pattern`: Input consumed by [`crate::runtime::generated::dynamic_dispatch_extract_param`].
238/// - `request_path`: Input consumed by [`crate::runtime::generated::dynamic_dispatch_extract_param`].
239/// - `param_name`: Input consumed by [`crate::runtime::generated::dynamic_dispatch_extract_param`].
240///
241/// # Returns
242///
243/// Returns [`Some`] when [`crate::runtime::generated::dynamic_dispatch_extract_param`] can produce a value and [`None`] otherwise.
244///
245/// # Performance
246///
247/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::dynamic_dispatch_extract_param`].
248///
249/// # Examples
250///
251/// ```rust,ignore
252/// let _result = pharos_native::runtime::generated::dynamic_dispatch_extract_param(todo!(), "/", todo!());
253/// ```
254///
255/// # Related
256///
257/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
258pub fn dynamic_dispatch_extract_param(
259    route_pattern: &str,
260    request_path: &str,
261    param_name: &str,
262) -> Option<String> {
263    if route_pattern.is_empty() || request_path.is_empty() || param_name.is_empty() {
264        return None;
265    }
266    let request_path = request_path.split('?').next().unwrap_or_default();
267    let pattern_segments: Vec<&str> = route_pattern.trim_matches('/').split('/').collect();
268    let request_segments: Vec<&str> = request_path.trim_matches('/').split('/').collect();
269    if pattern_segments.len() != request_segments.len() {
270        return None;
271    }
272    for (pattern, actual) in pattern_segments.iter().zip(request_segments.iter()) {
273        if let Some(name) = pattern.strip_prefix(':') {
274            if name == param_name {
275                return Some((*actual).to_owned());
276            }
277            continue;
278        }
279        if let Some(name) = pattern
280            .strip_prefix('{')
281            .and_then(|value| value.strip_suffix('}'))
282        {
283            if name == param_name {
284                return Some((*actual).to_owned());
285            }
286            continue;
287        }
288        if pattern != actual {
289            return None;
290        }
291    }
292    None
293}
294
295/// Performs dynamic find business by slug.
296///
297/// # Parameters
298///
299/// - `root`: Input consumed by [`crate::runtime::generated::dynamic_find_business_by_slug`].
300/// - `business_slug`: Input consumed by [`crate::runtime::generated::dynamic_find_business_by_slug`].
301///
302/// # Returns
303///
304/// Returns [`Some`] when [`crate::runtime::generated::dynamic_find_business_by_slug`] can produce a value and [`None`] otherwise.
305///
306/// # Performance
307///
308/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::dynamic_find_business_by_slug`].
309///
310/// # Examples
311///
312/// ```rust,ignore
313/// let _result = pharos_native::runtime::generated::dynamic_find_business_by_slug(std::path::Path::new("."), "demo");
314/// ```
315///
316/// # Related
317///
318/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
319pub fn dynamic_find_business_by_slug<'a>(
320    root: &'a Value,
321    business_slug: &str,
322) -> Option<&'a Value> {
323    pharos_runtime_business::dynamic_find_business_by_slug(root, business_slug)
324}
325
326/// Builds the runtime generated surface operation path.
327///
328/// # Parameters
329///
330/// - `surface_root`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_operation_path`].
331/// - `business_slug`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_operation_path`].
332/// - `operation_name`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_operation_path`].
333/// - `identity_value`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_operation_path`].
334///
335/// # Returns
336///
337/// Returns [`Some`] when [`crate::runtime::generated::runtime_generated_surface_operation_path`] can produce a value and [`None`] otherwise.
338///
339/// # Examples
340///
341/// ```rust,ignore
342/// let _result = pharos_native::runtime::generated::runtime_generated_surface_operation_path(todo!(), "demo", todo!(), todo!());
343/// ```
344///
345/// # Related
346///
347/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
348pub fn runtime_generated_surface_operation_path(
349    surface_root: &Value,
350    business_slug: Option<&str>,
351    operation_name: &str,
352    identity_value: Option<&str>,
353) -> Option<String> {
354    if operation_name.is_empty() || !surface_root.is_object() {
355        return None;
356    }
357    let template_path = surface_root
358        .get("operations")
359        .and_then(|value| value.get(operation_name))
360        .and_then(|value| value.get("path"))
361        .and_then(Value::as_str)
362        .filter(|value| !value.is_empty())?;
363    let context_param = surface_root
364        .get("context_binding")
365        .and_then(|value| value.get("param"))
366        .and_then(Value::as_str)
367        .unwrap_or("business_slug");
368    let identity_param = surface_root
369        .get("identity_param")
370        .and_then(Value::as_str)
371        .unwrap_or("id");
372    Some(
373        template_path
374            .replace(
375                &format!("{{{context_param}}}"),
376                business_slug.unwrap_or_default(),
377            )
378            .replace(
379                &format!("{{{identity_param}}}"),
380                identity_value.unwrap_or_default(),
381            ),
382    )
383}
384
385/// Renders template text.
386fn render_template_text(template_text: &str, params: &Map<String, Value>) -> Option<String> {
387    let mut out = String::with_capacity(template_text.len() + 64);
388    let mut cursor = 0usize;
389    while let Some(relative_open) = template_text[cursor..].find('{') {
390        let open = cursor + relative_open;
391        out.push_str(&template_text[cursor..open]);
392        let key_start = open + 1;
393        let relative_close = template_text[key_start..].find('}')?;
394        let close = key_start + relative_close;
395        let mut key = &template_text[key_start..close];
396        let raw_html = if let Some(stripped) = key.strip_prefix("html:") {
397            key = stripped;
398            true
399        } else if let Some(stripped) = key.strip_prefix("path:") {
400            key = stripped;
401            false
402        } else if let Some(stripped) = key.strip_prefix("url:") {
403            key = stripped;
404            false
405        } else {
406            false
407        };
408        let value = params
409            .get(key)
410            .map_or_else(String::new, |value| match value {
411                Value::Null => String::new(),
412                Value::String(text) => text.clone(),
413                Value::Bool(flag) => flag.to_string(),
414                Value::Number(number) => number.to_string(),
415                _ => serde_json::to_string(value).unwrap_or_default(),
416            });
417        if raw_html {
418            out.push_str(&value);
419        } else {
420            out.push_str(
421                &value
422                    .replace('&', "&amp;")
423                    .replace('<', "&lt;")
424                    .replace('>', "&gt;")
425                    .replace('"', "&quot;"),
426            );
427        }
428        cursor = close + 1;
429    }
430    out.push_str(&template_text[cursor..]);
431    Some(out)
432}
433
434/// Renders generated admin fragment from value runtime.
435///
436/// # Parameters
437///
438/// - `app_root`: Input consumed by [`crate::runtime::generated::render_generated_admin_fragment_from_value_runtime`].
439/// - `relative_path`: Input consumed by [`crate::runtime::generated::render_generated_admin_fragment_from_value_runtime`].
440/// - `params`: Input consumed by [`crate::runtime::generated::render_generated_admin_fragment_from_value_runtime`].
441///
442/// # Returns
443///
444/// Returns [`Some`] when [`crate::runtime::generated::render_generated_admin_fragment_from_value_runtime`] can produce a value and [`None`] otherwise.
445///
446/// # Performance
447///
448/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::render_generated_admin_fragment_from_value_runtime`].
449///
450/// # Examples
451///
452/// ```rust,ignore
453/// let _result = pharos_native::runtime::generated::render_generated_admin_fragment_from_value_runtime(std::path::Path::new("apps/dev_docs"), "page", todo!());
454/// ```
455///
456/// # Related
457///
458/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
459pub fn render_generated_admin_fragment_from_value_runtime(
460    app_root: &Path,
461    relative_path: &str,
462    params: &Map<String, Value>,
463) -> Option<String> {
464    if relative_path.is_empty() || app_root.as_os_str().is_empty() {
465        return None;
466    }
467    let template_text =
468        super::cache::template_source_lookup(app_root, relative_path).or_else(|| {
469            let text = fs::read_to_string(app_root.join(relative_path)).ok()?;
470            super::cache::template_source_store(app_root, relative_path, &text);
471            Some(text)
472        })?;
473    render_template_text(&template_text, params)
474}
475
476/// Builds the runtime generated surface append record actions HTML.
477///
478/// # Parameters
479///
480/// - `buffer`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_append_record_actions_html`].
481/// - `app_root`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_append_record_actions_html`].
482/// - `edit_path`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_append_record_actions_html`].
483/// - `delete_path`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_append_record_actions_html`].
484/// - `delete_button_label`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_append_record_actions_html`].
485///
486/// # Returns
487///
488/// Returns `true` when the condition checked by [`crate::runtime::generated::runtime_generated_surface_append_record_actions_html`] holds and `false` otherwise.
489///
490/// # Examples
491///
492/// ```rust,ignore
493/// let _result = pharos_native::runtime::generated::runtime_generated_surface_append_record_actions_html(&mut [], std::path::Path::new("apps/dev_docs"), todo!(), todo!(), todo!());
494/// ```
495///
496/// # Related
497///
498/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
499pub fn runtime_generated_surface_append_record_actions_html(
500    buffer: &mut String,
501    app_root: &Path,
502    edit_path: Option<&str>,
503    delete_path: Option<&str>,
504    delete_button_label: Option<&str>,
505) -> bool {
506    if app_root.as_os_str().is_empty() {
507        return false;
508    }
509    let mut edit_link_html = String::new();
510    let mut delete_link_html = String::new();
511
512    if let Some(edit_path) = edit_path.filter(|value| !value.is_empty()) {
513        let params = Map::from_iter([
514            (
515                "button_class".to_owned(),
516                Value::String("btn-secondary".to_owned()),
517            ),
518            (
519                "action_path".to_owned(),
520                Value::String(edit_path.to_owned()),
521            ),
522            ("button_label".to_owned(), Value::String("Edit".to_owned())),
523        ]);
524        let Some(rendered) = render_generated_admin_fragment_from_value_runtime(
525            app_root,
526            "templates/shared/fragments/action_link.html",
527            &params,
528        ) else {
529            return false;
530        };
531        edit_link_html = rendered;
532    }
533
534    if let Some(delete_path) = delete_path.filter(|value| !value.is_empty()) {
535        let params = Map::from_iter([
536            (
537                "button_class".to_owned(),
538                Value::String("btn-danger".to_owned()),
539            ),
540            (
541                "action_path".to_owned(),
542                Value::String(delete_path.to_owned()),
543            ),
544            (
545                "button_label".to_owned(),
546                Value::String(
547                    delete_button_label
548                        .filter(|value| !value.is_empty())
549                        .unwrap_or("Delete")
550                        .to_owned(),
551                ),
552            ),
553        ]);
554        let Some(rendered) = render_generated_admin_fragment_from_value_runtime(
555            app_root,
556            "templates/shared/fragments/action_link.html",
557            &params,
558        ) else {
559            return false;
560        };
561        delete_link_html = rendered;
562    }
563
564    let params = Map::from_iter([
565        (
566            "primary_action_html".to_owned(),
567            Value::String(edit_link_html),
568        ),
569        (
570            "secondary_action_html".to_owned(),
571            Value::String(delete_link_html),
572        ),
573    ]);
574    let Some(row_html) = render_generated_admin_fragment_from_value_runtime(
575        app_root,
576        "templates/shared/fragments/action_row.html",
577        &params,
578    ) else {
579        return false;
580    };
581    buffer.push_str(&row_html);
582    true
583}
584
585/// Builds the runtime generated surface append table header cells HTML.
586///
587/// # Parameters
588///
589/// - `buffer`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_append_table_header_cells_html`].
590/// - `columns`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_append_table_header_cells_html`].
591/// - `app_root`: Input consumed by [`crate::runtime::generated::runtime_generated_surface_append_table_header_cells_html`].
592///
593/// # Returns
594///
595/// Returns `true` when the condition checked by [`crate::runtime::generated::runtime_generated_surface_append_table_header_cells_html`] holds and `false` otherwise.
596///
597/// # Performance
598///
599/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::runtime_generated_surface_append_table_header_cells_html`].
600///
601/// # Examples
602///
603/// ```rust,ignore
604/// let _result = pharos_native::runtime::generated::runtime_generated_surface_append_table_header_cells_html(&mut [], todo!(), std::path::Path::new("apps/dev_docs"));
605/// ```
606///
607/// # Related
608///
609/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
610pub fn runtime_generated_surface_append_table_header_cells_html(
611    buffer: &mut String,
612    columns: &Value,
613    app_root: &Path,
614) -> bool {
615    if app_root.as_os_str().is_empty() {
616        return false;
617    }
618    let Some(columns) = columns.as_array() else {
619        return true;
620    };
621    for column in columns {
622        let mut column_label = column
623            .get("label")
624            .and_then(Value::as_str)
625            .map(ToOwned::to_owned)
626            .unwrap_or_default();
627        if column_label.is_empty() {
628            column_label = generated_surface_label(column.get("field").and_then(Value::as_str))
629                .unwrap_or_default();
630        }
631        if column_label.is_empty() {
632            return false;
633        }
634        let params = Map::from_iter([("column_label".to_owned(), Value::String(column_label))]);
635        let Some(html) = render_generated_admin_fragment_from_value_runtime(
636            app_root,
637            "templates/generated_admin/fragments/table_header_cell.html",
638            &params,
639        ) else {
640            return false;
641        };
642        buffer.push_str(&html);
643    }
644    true
645}
646
647/// Performs generated surface admin create load.
648///
649/// # Parameters
650///
651/// - `app`: Input consumed by [`crate::runtime::generated::generated_surface_admin_create_load`].
652/// - `surface_kind`: Input consumed by [`crate::runtime::generated::generated_surface_admin_create_load`].
653/// - `request_path`: Input consumed by [`crate::runtime::generated::generated_surface_admin_create_load`].
654/// - `route_pattern`: Input consumed by [`crate::runtime::generated::generated_surface_admin_create_load`].
655///
656/// # Returns
657///
658/// Returns [`Some`] when [`crate::runtime::generated::generated_surface_admin_create_load`] can produce a value and [`None`] otherwise.
659///
660/// # Performance
661///
662/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::generated_surface_admin_create_load`].
663///
664/// # Examples
665///
666/// ```rust,ignore
667/// let _result = pharos_native::runtime::generated::generated_surface_admin_create_load(todo!(), todo!(), "/", todo!());
668/// ```
669///
670/// # Related
671///
672/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
673pub fn generated_surface_admin_create_load(
674    app: &AppRuntime,
675    surface_kind: &str,
676    request_path: &str,
677    route_pattern: &str,
678) -> Option<GeneratedSurfaceApiMatchRuntime> {
679    if surface_kind.is_empty() || request_path.is_empty() || route_pattern.is_empty() {
680        return None;
681    }
682    let app_root = app.app_root.as_deref().or(app.artifact_dir.as_deref())?;
683    if super::database::relational_runtime_authority_enabled(Path::new(app_root)) {
684        return None;
685    }
686    let generated_fixture_path = generated_surface_fixture_path(app)?;
687    let form_fixture_path = fixture_path(app, "data/contracts/forms.json");
688    let fixture_root = read_json_file(Path::new(&generated_fixture_path))?;
689    let state_root = read_json_file(Path::new(app.state_path.as_deref()?))?;
690    let form_root = form_fixture_path
691        .as_deref()
692        .and_then(|path| read_json_file(Path::new(path)));
693    let surface_root = runtime_generated_admin_surface_find(&fixture_root, surface_kind)?.clone();
694    let context_param = surface_root
695        .get("context_binding")
696        .and_then(|value| value.get("param"))
697        .and_then(Value::as_str)?;
698    let match_field = surface_root
699        .get("context_binding")
700        .and_then(|value| value.get("match_field"))
701        .and_then(Value::as_str)?;
702    let business_slug = dynamic_dispatch_extract_param(route_pattern, request_path, context_param)?;
703    let _expected_create_path = runtime_generated_surface_operation_path(
704        &surface_root,
705        Some(&business_slug),
706        "create",
707        None,
708    )?;
709    if match_field.is_empty() {
710        return None;
711    }
712    let business = dynamic_find_business_by_slug(&state_root, &business_slug)?;
713    let context_root = Value::Object(Map::from_iter([(
714        context_param.to_owned(),
715        Value::String(business_slug.clone()),
716    )]));
717    let context_id = json_long(business, "id", 0);
718    Some(GeneratedSurfaceApiMatchRuntime {
719        fixture_root,
720        state_root: state_root.clone(),
721        form_root,
722        surface_root: surface_root.clone(),
723        context_root,
724        surface_id: surface_root
725            .get("surface_id")
726            .and_then(Value::as_str)
727            .map(ToOwned::to_owned),
728        operation_name: Some("create".to_owned()),
729        business_slug: Some(business_slug),
730        fixture_path: Some(generated_fixture_path),
731        form_fixture_path,
732        context_id,
733        tenant_id: tenant_id_for_business_base(&state_root, context_id),
734    })
735}
736
737/// Performs generated surface validation report empty.
738///
739/// # Parameters
740///
741/// - `form_id`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report_empty`].
742/// - `action_id`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report_empty`].
743/// - `actor_scope`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report_empty`].
744/// - `tenant_scope`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report_empty`].
745///
746/// # Returns
747///
748/// Returns [`Some`] when [`crate::runtime::generated::generated_surface_validation_report_empty`] can produce a value and [`None`] otherwise.
749///
750/// # Examples
751///
752/// ```rust,ignore
753/// let _result = pharos_native::runtime::generated::generated_surface_validation_report_empty("form", todo!(), todo!(), todo!());
754/// ```
755///
756/// # Related
757///
758/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
759pub fn generated_surface_validation_report_empty(
760    form_id: Option<&str>,
761    action_id: Option<&str>,
762    actor_scope: Option<&str>,
763    tenant_scope: Option<&str>,
764) -> Option<String> {
765    serde_json::to_string(&serde_json::json!({
766        "schema": "pharos.validation.report.v1",
767        "report_id": "pharos.validation.report.v1",
768        "form_id": form_id.unwrap_or_default(),
769        "action_id": action_id.unwrap_or_default(),
770        "field_errors": [],
771        "form_errors": [],
772        "actor_scope": actor_scope.unwrap_or("anonymous"),
773        "tenant_scope": tenant_scope.unwrap_or("global"),
774    }))
775    .ok()
776}
777
778/// Performs generated surface validation report add field error.
779///
780/// # Parameters
781///
782/// - `report_json`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report_add_field_error`].
783/// - `field_id`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report_add_field_error`].
784/// - `rule_id`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report_add_field_error`].
785/// - `code`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report_add_field_error`].
786/// - `message`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report_add_field_error`].
787///
788/// # Returns
789///
790/// Returns [`Some`] when [`crate::runtime::generated::generated_surface_validation_report_add_field_error`] can produce a value and [`None`] otherwise.
791///
792/// # Performance
793///
794/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::generated_surface_validation_report_add_field_error`].
795///
796/// # Examples
797///
798/// ```rust,ignore
799/// let _result = pharos_native::runtime::generated::generated_surface_validation_report_add_field_error("{}", "field", todo!(), todo!(), "example");
800/// ```
801///
802/// # Related
803///
804/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
805pub fn generated_surface_validation_report_add_field_error(
806    report_json: &str,
807    field_id: Option<&str>,
808    rule_id: Option<&str>,
809    code: Option<&str>,
810    message: Option<&str>,
811) -> Option<String> {
812    let mut root = serde_json::from_str::<Value>(if report_json.is_empty() {
813        "{}"
814    } else {
815        report_json
816    })
817    .ok()?;
818    let root_obj = root.as_object_mut()?;
819    let field_errors = root_obj
820        .entry("field_errors".to_owned())
821        .or_insert_with(|| Value::Array(Vec::new()))
822        .as_array_mut()?;
823    field_errors.push(serde_json::json!({
824        "field_id": field_id.unwrap_or_default(),
825        "rule_id": rule_id.unwrap_or("validation_rule"),
826        "code": code.unwrap_or("invalid"),
827        "message": message.unwrap_or("Invalid field value."),
828    }));
829    serde_json::to_string(&root).ok()
830}
831
832/// Performs generated surface submitted JSON.
833///
834/// # Parameters
835///
836/// - `surface_root`: Input consumed by [`crate::runtime::generated::generated_surface_submitted_json`].
837/// - `form_root`: Input consumed by [`crate::runtime::generated::generated_surface_submitted_json`].
838/// - `payload_root`: Input consumed by [`crate::runtime::generated::generated_surface_submitted_json`].
839///
840/// # Returns
841///
842/// Returns [`Some`] when [`crate::runtime::generated::generated_surface_submitted_json`] can produce a value and [`None`] otherwise.
843///
844/// # Performance
845///
846/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::generated_surface_submitted_json`].
847///
848/// # Examples
849///
850/// ```rust,ignore
851/// let _result = pharos_native::runtime::generated::generated_surface_submitted_json(todo!(), todo!(), todo!());
852/// ```
853///
854/// # Related
855///
856/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
857pub fn generated_surface_submitted_json(
858    surface_root: &Value,
859    form_root: &Value,
860    payload_root: &Value,
861) -> Option<String> {
862    let form_id = surface_root.get("form_id").and_then(Value::as_str)?;
863    let form = form_root
864        .get("forms")
865        .and_then(Value::as_array)?
866        .iter()
867        .find(|item| item.get("form_id").and_then(Value::as_str) == Some(form_id))?;
868    let mut out = Map::new();
869    if let Some(fields) = form.get("fields").and_then(Value::as_array) {
870        for item in fields {
871            let Some(field_id) = item.get("field_id").and_then(Value::as_str) else {
872                continue;
873            };
874            if let Some(value) = payload_root.get(field_id) {
875                out.insert(field_id.to_owned(), value.clone());
876            }
877        }
878    }
879    if let Some(relation_inputs) = surface_root
880        .get("relation_inputs")
881        .and_then(Value::as_array)
882    {
883        for item in relation_inputs {
884            let Some(field_name) = item.get("field").and_then(Value::as_str) else {
885                continue;
886            };
887            if let Some(value) = payload_root.get(field_name) {
888                out.insert(field_name.to_owned(), value.clone());
889            }
890        }
891    }
892    serde_json::to_string(&Value::Object(out)).ok()
893}
894
895/// Performs generated surface coerce candidate JSON.
896///
897/// # Parameters
898///
899/// - `surface_root`: Input consumed by [`crate::runtime::generated::generated_surface_coerce_candidate_json`].
900/// - `candidate_json`: Input consumed by [`crate::runtime::generated::generated_surface_coerce_candidate_json`].
901///
902/// # Returns
903///
904/// Returns [`Some`] when [`crate::runtime::generated::generated_surface_coerce_candidate_json`] can produce a value and [`None`] otherwise.
905///
906/// # Performance
907///
908/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::generated_surface_coerce_candidate_json`].
909///
910/// # Examples
911///
912/// ```rust,ignore
913/// let _result = pharos_native::runtime::generated::generated_surface_coerce_candidate_json(todo!(), todo!());
914/// ```
915///
916/// # Related
917///
918/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
919pub fn generated_surface_coerce_candidate_json(
920    surface_root: &Value,
921    candidate_json: &str,
922) -> Option<String> {
923    let candidate_root = serde_json::from_str::<Value>(if candidate_json.is_empty() {
924        "{}"
925    } else {
926        candidate_json
927    })
928    .ok()?;
929    let mut out = candidate_root.as_object()?.clone();
930    let Some(field_types) = surface_root.get("field_types").and_then(Value::as_object) else {
931        return serde_json::to_string(&Value::Object(out)).ok();
932    };
933    for (field_name, type_value) in field_types {
934        let Some(candidate_value) = out.get(field_name).cloned() else {
935            continue;
936        };
937        let type_text = value_to_string(type_value);
938        if type_text == "integer" {
939            let text = value_to_string(&candidate_value);
940            if digits_only_text(&text) {
941                out.insert(
942                    field_name.clone(),
943                    Value::Number(text.parse::<i64>().ok()?.into()),
944                );
945            }
946        } else if type_text == "boolean" {
947            if !candidate_value.is_boolean() {
948                out.insert(
949                    field_name.clone(),
950                    Value::Bool(truthy_text(&value_to_string(&candidate_value))),
951                );
952            }
953        } else if type_text == "array_integer" && candidate_value.is_array() {
954            let mut new_array = Vec::new();
955            for entry in candidate_value.as_array()? {
956                let text = value_to_string(entry);
957                if digits_only_text(&text) {
958                    new_array.push(Value::Number(text.parse::<i64>().ok()?.into()));
959                }
960            }
961            out.insert(field_name.clone(), Value::Array(new_array));
962        }
963    }
964    serde_json::to_string(&Value::Object(out)).ok()
965}
966
967/// Performs next array ID runtime local.
968///
969/// # Parameters
970///
971/// - `rows`: Input consumed by [`crate::runtime::generated::next_array_id_runtime_local`].
972///
973/// # Returns
974///
975/// Returns the computed value produced by [`crate::runtime::generated::next_array_id_runtime_local`].
976///
977/// # Performance
978///
979/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::next_array_id_runtime_local`].
980///
981/// # Examples
982///
983/// ```rust,ignore
984/// let _result = pharos_native::runtime::generated::next_array_id_runtime_local(todo!());
985/// ```
986///
987/// # Related
988///
989/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
990pub fn next_array_id_runtime_local(rows: Option<&Value>) -> i64 {
991    let mut next_id = 1i64;
992    if let Some(rows) = rows.and_then(Value::as_array) {
993        for row in rows {
994            let existing_id = json_long(row, "id", 0);
995            if existing_id >= next_id {
996                next_id = existing_id + 1;
997            }
998        }
999    }
1000    next_id
1001}
1002
1003/// Performs generated surface apply defaults to mut object.
1004///
1005/// # Parameters
1006///
1007/// - `target_obj`: Input consumed by [`crate::runtime::generated::generated_surface_apply_defaults_to_mut_object`].
1008/// - `defaults_root`: Input consumed by [`crate::runtime::generated::generated_surface_apply_defaults_to_mut_object`].
1009///
1010/// # Returns
1011///
1012/// Returns `true` when the condition checked by [`crate::runtime::generated::generated_surface_apply_defaults_to_mut_object`] holds and `false` otherwise.
1013///
1014/// # Performance
1015///
1016/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::generated_surface_apply_defaults_to_mut_object`].
1017///
1018/// # Examples
1019///
1020/// ```rust,ignore
1021/// let _result = pharos_native::runtime::generated::generated_surface_apply_defaults_to_mut_object(todo!(), todo!());
1022/// ```
1023///
1024/// # Related
1025///
1026/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
1027pub fn generated_surface_apply_defaults_to_mut_object(
1028    target_obj: &mut Map<String, Value>,
1029    defaults_root: Option<&Value>,
1030) -> bool {
1031    let Some(defaults_root) = defaults_root.and_then(Value::as_object) else {
1032        return true;
1033    };
1034    for (field_name, value) in defaults_root {
1035        if !target_obj.contains_key(field_name) {
1036            target_obj.insert(field_name.clone(), value.clone());
1037        }
1038    }
1039    true
1040}
1041
1042/// Performs generated surface validation report has errors.
1043///
1044/// # Parameters
1045///
1046/// - `report_json`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report_has_errors`].
1047///
1048/// # Returns
1049///
1050/// Returns `true` when the condition checked by [`crate::runtime::generated::generated_surface_validation_report_has_errors`] holds and `false` otherwise.
1051///
1052/// # Performance
1053///
1054/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::generated_surface_validation_report_has_errors`].
1055///
1056/// # Examples
1057///
1058/// ```rust,ignore
1059/// let _result = pharos_native::runtime::generated::generated_surface_validation_report_has_errors("{}");
1060/// ```
1061///
1062/// # Related
1063///
1064/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
1065pub fn generated_surface_validation_report_has_errors(report_json: &str) -> bool {
1066    let Ok(root) = serde_json::from_str::<Value>(if report_json.is_empty() {
1067        "{}"
1068    } else {
1069        report_json
1070    }) else {
1071        return false;
1072    };
1073    let field_errors = root
1074        .get("field_errors")
1075        .and_then(Value::as_array)
1076        .map(Vec::len)
1077        .unwrap_or(0);
1078    let form_errors = root
1079        .get("form_errors")
1080        .and_then(Value::as_array)
1081        .map(Vec::len)
1082        .unwrap_or(0);
1083    field_errors + form_errors > 0
1084}
1085
1086#[derive(Debug, Clone, Eq, PartialEq)]
1087/// Public enum `GeneratedSurfaceCreateOutcome` used by this module.
1088///
1089/// # Variants
1090///
1091/// - `UnprocessableEntity`: Supported `GeneratedSurfaceCreateOutcome` variant.
1092/// - `Redirect`: Supported `GeneratedSurfaceCreateOutcome` variant.
1093///
1094/// # Examples
1095///
1096/// ```rust,ignore
1097/// let _ = pharos_native::runtime::generated::GeneratedSurfaceCreateOutcome::UnprocessableEntity;
1098/// ```
1099///
1100/// # Related
1101///
1102/// - [`crate::runtime::generated`] for constructors, helpers, and neighboring runtime types.
1103pub enum GeneratedSurfaceCreateOutcome {
1104    UnprocessableEntity { report_json: String },
1105    Redirect { location: String },
1106}
1107
1108/// Performs persist generated surface admin create runtime.
1109///
1110/// # Parameters
1111///
1112/// - `app`: Input consumed by [`crate::runtime::generated::persist_generated_surface_admin_create_runtime`].
1113/// - `match_record`: Input consumed by [`crate::runtime::generated::persist_generated_surface_admin_create_runtime`].
1114/// - `candidate_root`: Input consumed by [`crate::runtime::generated::persist_generated_surface_admin_create_runtime`].
1115///
1116/// # Returns
1117///
1118/// Returns [`Some`] when [`crate::runtime::generated::persist_generated_surface_admin_create_runtime`] can produce a value and [`None`] otherwise.
1119///
1120/// # Performance
1121///
1122/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::persist_generated_surface_admin_create_runtime`].
1123///
1124/// # Examples
1125///
1126/// ```rust,ignore
1127/// let _result = pharos_native::runtime::generated::persist_generated_surface_admin_create_runtime(todo!(), todo!(), todo!());
1128/// ```
1129///
1130/// # Related
1131///
1132/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
1133pub fn persist_generated_surface_admin_create_runtime(
1134    app: &AppRuntime,
1135    match_record: &GeneratedSurfaceApiMatchRuntime,
1136    candidate_root: &Value,
1137) -> Option<String> {
1138    let app_root = app.app_root.as_deref().or(app.artifact_dir.as_deref())?;
1139    if super::database::relational_runtime_authority_enabled(Path::new(app_root)) {
1140        return None;
1141    }
1142    let collection_name = match_record
1143        .surface_root
1144        .get("entity_collection")
1145        .and_then(Value::as_str)
1146        .filter(|value| !value.is_empty())?;
1147    let identity_field = match_record
1148        .surface_root
1149        .get("identity_field")
1150        .and_then(Value::as_str)
1151        .filter(|value| !value.is_empty())
1152        .unwrap_or("id");
1153    let scope_field = match_record
1154        .surface_root
1155        .get("scope_field")
1156        .and_then(Value::as_str)
1157        .filter(|value| !value.is_empty());
1158    let auto_sequence_field = match_record
1159        .surface_root
1160        .get("auto_sequence_field")
1161        .and_then(Value::as_str)
1162        .filter(|value| !value.is_empty());
1163    let mut state_root = match_record.state_root.clone();
1164    let state_map = state_root.as_object_mut()?;
1165    let source_rows = state_map.get(collection_name).cloned();
1166    let section = state_map
1167        .entry(collection_name.to_owned())
1168        .or_insert_with(|| Value::Array(Vec::new()));
1169    let section_rows = section.as_array_mut()?;
1170    let mut item_mut = candidate_root.as_object()?.clone();
1171    generated_surface_apply_defaults_to_mut_object(
1172        &mut item_mut,
1173        match_record.surface_root.get("defaults"),
1174    );
1175    if let Some(scope_field) = scope_field {
1176        item_mut.insert(
1177            scope_field.to_owned(),
1178            Value::Number(match_record.context_id.into()),
1179        );
1180    }
1181    if let Some(auto_sequence_field) = auto_sequence_field
1182        && !item_mut.contains_key(auto_sequence_field)
1183    {
1184        let mut next_sequence = 1i64;
1185        if let Some(rows) = source_rows.as_ref().and_then(Value::as_array) {
1186            for row in rows {
1187                let value = json_long(row, auto_sequence_field, 0);
1188                let scope_value = json_long(row, scope_field.unwrap_or("business_id"), 0);
1189                if scope_value == match_record.context_id && value >= next_sequence {
1190                    next_sequence = value + 1;
1191                }
1192            }
1193        }
1194        item_mut.insert(
1195            auto_sequence_field.to_owned(),
1196            Value::Number(next_sequence.into()),
1197        );
1198    }
1199    let record_id = next_array_id_runtime_local(source_rows.as_ref());
1200    item_mut.insert(identity_field.to_owned(), Value::Number(record_id.into()));
1201    section_rows.push(Value::Object(item_mut));
1202    let state_path = Path::new(app.state_path.as_deref()?);
1203    write_json_file(state_path, &state_root).then(|| {
1204        runtime_generated_surface_operation_path(
1205            &match_record.surface_root,
1206            match_record.business_slug.as_deref(),
1207            "manage",
1208            None,
1209        )
1210        .unwrap_or_else(|| "/".to_owned())
1211    })
1212}
1213
1214/// Handles generated surface admin create request.
1215///
1216/// # Parameters
1217///
1218/// - `app`: Input consumed by [`crate::runtime::generated::handle_generated_surface_admin_create_request`].
1219/// - `match_record`: Input consumed by [`crate::runtime::generated::handle_generated_surface_admin_create_request`].
1220/// - `candidate_root`: Input consumed by [`crate::runtime::generated::handle_generated_surface_admin_create_request`].
1221/// - `report_json`: Input consumed by [`crate::runtime::generated::handle_generated_surface_admin_create_request`].
1222///
1223/// # Returns
1224///
1225/// Returns [`Some`] when [`crate::runtime::generated::handle_generated_surface_admin_create_request`] can produce a value and [`None`] otherwise.
1226///
1227/// # Examples
1228///
1229/// ```rust,ignore
1230/// let _result = pharos_native::runtime::generated::handle_generated_surface_admin_create_request(todo!(), todo!(), todo!(), "{}");
1231/// ```
1232///
1233/// # Related
1234///
1235/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
1236pub fn handle_generated_surface_admin_create_request(
1237    app: &AppRuntime,
1238    match_record: &GeneratedSurfaceApiMatchRuntime,
1239    candidate_root: &Value,
1240    report_json: &str,
1241) -> Option<GeneratedSurfaceCreateOutcome> {
1242    if generated_surface_validation_report_has_errors(report_json) {
1243        return Some(GeneratedSurfaceCreateOutcome::UnprocessableEntity {
1244            report_json: report_json.to_owned(),
1245        });
1246    }
1247    let redirect_path =
1248        persist_generated_surface_admin_create_runtime(app, match_record, candidate_root)?;
1249    let base_path = app.base_path.clone().unwrap_or_default();
1250    Some(GeneratedSurfaceCreateOutcome::Redirect {
1251        location: format!("{base_path}{redirect_path}"),
1252    })
1253}
1254
1255/// Writes JSON file.
1256///
1257/// # Parameters
1258///
1259/// - `path`: Input consumed by [`crate::runtime::generated::write_json_file`].
1260/// - `value`: Input consumed by [`crate::runtime::generated::write_json_file`].
1261///
1262/// # Returns
1263///
1264/// Returns `true` when the condition checked by [`crate::runtime::generated::write_json_file`] holds and `false` otherwise.
1265///
1266/// # Examples
1267///
1268/// ```rust,ignore
1269/// let _result = pharos_native::runtime::generated::write_json_file(std::path::Path::new("fixture.json"), "value");
1270/// ```
1271///
1272/// # Related
1273///
1274/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
1275pub fn write_json_file(path: &Path, value: &Value) -> bool {
1276    if let Some(parent) = path.parent()
1277        && fs::create_dir_all(parent).is_err()
1278    {
1279        return false;
1280    }
1281    serde_json::to_string_pretty(value)
1282        .ok()
1283        .and_then(|text| super::fs::write_binary_file(path, text.as_bytes(), 0o640).ok())
1284        .is_some()
1285}
1286
1287/// Performs generated surface delete button label.
1288///
1289/// # Parameters
1290///
1291/// - `surface`: Input consumed by [`crate::runtime::generated::generated_surface_delete_button_label`].
1292///
1293/// # Returns
1294///
1295/// Returns the computed value produced by [`crate::runtime::generated::generated_surface_delete_button_label`].
1296///
1297/// # Examples
1298///
1299/// ```rust,ignore
1300/// let _result = pharos_native::runtime::generated::generated_surface_delete_button_label(todo!());
1301/// ```
1302///
1303/// # Related
1304///
1305/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
1306pub fn generated_surface_delete_button_label(surface: &Value) -> String {
1307    surface
1308        .get("admin")
1309        .and_then(|value| value.get("delete_button_label"))
1310        .and_then(Value::as_str)
1311        .filter(|value| !value.is_empty())
1312        .unwrap_or("Delete")
1313        .to_owned()
1314}
1315
1316/// Renders one generated-surface operation path with context and identity values.
1317///
1318/// # Parameters
1319///
1320/// - `surface`: Input consumed by [`crate::runtime::generated::generated_surface_operation_path`].
1321/// - `context_value`: Input consumed by [`crate::runtime::generated::generated_surface_operation_path`].
1322/// - `operation_name`: Input consumed by [`crate::runtime::generated::generated_surface_operation_path`].
1323/// - `identity_value`: Input consumed by [`crate::runtime::generated::generated_surface_operation_path`].
1324///
1325/// # Returns
1326///
1327/// Returns [`Some`] when [`crate::runtime::generated::generated_surface_operation_path`] can produce a value and [`None`] otherwise.
1328///
1329/// # Examples
1330///
1331/// ```rust,ignore
1332/// let _result = pharos_native::runtime::generated::generated_surface_operation_path(todo!(), todo!(), todo!(), todo!());
1333/// ```
1334///
1335/// # Related
1336///
1337/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
1338pub fn generated_surface_operation_path(
1339    surface: &Value,
1340    context_value: Option<&str>,
1341    operation_name: &str,
1342    identity_value: Option<&str>,
1343) -> Option<String> {
1344    if operation_name.is_empty() {
1345        return None;
1346    }
1347    let path = surface
1348        .get("operations")?
1349        .get(operation_name)?
1350        .get("path")?
1351        .as_str()?;
1352    let context_param = surface
1353        .get("context_binding")
1354        .and_then(|value| value.get("param"))
1355        .and_then(Value::as_str)
1356        .unwrap_or("business_slug");
1357    let identity_param = surface
1358        .get("identity_param")
1359        .and_then(Value::as_str)
1360        .unwrap_or("id");
1361    Some(
1362        path.replace(
1363            &format!("{{{context_param}}}"),
1364            context_value.unwrap_or_default(),
1365        )
1366        .replace(
1367            &format!("{{{identity_param}}}"),
1368            identity_value.unwrap_or_default(),
1369        ),
1370    )
1371}
1372
1373/// Copies fragment tree for runtime.
1374///
1375/// # Parameters
1376///
1377/// - `source_root`: Input consumed by [`crate::runtime::generated::copy_fragment_tree_for_runtime`].
1378/// - `app_root`: Input consumed by [`crate::runtime::generated::copy_fragment_tree_for_runtime`].
1379///
1380/// # Returns
1381///
1382/// Returns `true` when the condition checked by [`crate::runtime::generated::copy_fragment_tree_for_runtime`] holds and `false` otherwise.
1383///
1384/// # Performance
1385///
1386/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::copy_fragment_tree_for_runtime`].
1387///
1388/// # Examples
1389///
1390/// ```rust,ignore
1391/// let _result = pharos_native::runtime::generated::copy_fragment_tree_for_runtime(todo!(), std::path::Path::new("apps/dev_docs"));
1392/// ```
1393///
1394/// # Related
1395///
1396/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
1397pub fn copy_fragment_tree_for_runtime(source_root: &Path, app_root: &Path) -> bool {
1398    let mappings = [
1399        (
1400            source_root.join("templates/shared/fragments/action_link.html"),
1401            app_root.join("templates/shared/fragments/action_link.html"),
1402        ),
1403        (
1404            source_root.join("templates/shared/fragments/action_row.html"),
1405            app_root.join("templates/shared/fragments/action_row.html"),
1406        ),
1407        (
1408            source_root.join("templates/generated_admin/fragments/table_header_cell.html"),
1409            app_root.join("templates/generated_admin/fragments/table_header_cell.html"),
1410        ),
1411    ];
1412    for (source, dest) in mappings {
1413        let Ok(text) = fs::read_to_string(&source) else {
1414            return false;
1415        };
1416        if let Some(parent) = dest.parent()
1417            && fs::create_dir_all(parent).is_err()
1418        {
1419            return false;
1420        }
1421        if super::fs::write_file_within(app_root, &dest, text.as_bytes(), 0o644).is_err() {
1422            return false;
1423        }
1424    }
1425    true
1426}
1427
1428/// Matches a generated operation against a bounded method and route pattern.
1429///
1430/// # Parameters
1431///
1432/// - `operation_root`: Input consumed by [`crate::runtime::generated::generated_surface_operation_matches`].
1433/// - `request_method`: Input consumed by [`crate::runtime::generated::generated_surface_operation_matches`].
1434/// - `request_path`: Input consumed by [`crate::runtime::generated::generated_surface_operation_matches`].
1435///
1436/// # Returns
1437///
1438/// Returns [`Some`] when [`crate::runtime::generated::generated_surface_operation_matches`] can produce a value and [`None`] otherwise.
1439///
1440/// # Examples
1441///
1442/// ```rust,ignore
1443/// let _result = pharos_native::runtime::generated::generated_surface_operation_matches(todo!(), todo!(), "/");
1444/// ```
1445///
1446/// # Related
1447///
1448/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
1449pub fn generated_surface_operation_matches(
1450    operation_root: &Value,
1451    request_method: &str,
1452    request_path: &str,
1453) -> Option<String> {
1454    let method = operation_root
1455        .get("method")
1456        .and_then(Value::as_str)
1457        .unwrap_or_default();
1458    let pattern = operation_root
1459        .get("path")
1460        .or_else(|| operation_root.get("route_pattern"))
1461        .and_then(Value::as_str)?;
1462    (method.eq_ignore_ascii_case(request_method)
1463        && super::dynamic::pattern_matches(pattern, request_path))
1464    .then(|| pattern.to_owned())
1465}
1466
1467/// Builds a validation report from the declared surface and candidate payload.
1468///
1469/// # Parameters
1470///
1471/// - `surface_root`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report`].
1472/// - `form_root`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report`].
1473/// - `state_root`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report`].
1474/// - `context_id`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report`].
1475/// - `tenant_id`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report`].
1476/// - `candidate_json`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report`].
1477/// - `current_id_text`: Input consumed by [`crate::runtime::generated::generated_surface_validation_report`].
1478///
1479/// # Returns
1480///
1481/// Returns [`Some`] when [`crate::runtime::generated::generated_surface_validation_report`] can produce a value and [`None`] otherwise.
1482///
1483/// # Performance
1484///
1485/// Performance is proportional to the size of the supplied inputs and any parsed or loaded data consumed by [`crate::runtime::generated::generated_surface_validation_report`].
1486///
1487/// # Examples
1488///
1489/// ```rust,ignore
1490/// let _result = pharos_native::runtime::generated::generated_surface_validation_report(todo!(), todo!(), todo!(), todo!(), todo!(), todo!(), todo!());
1491/// ```
1492///
1493/// # Related
1494///
1495/// - [`crate::runtime::generated`] for adjacent helpers that share the same runtime surface.
1496pub fn generated_surface_validation_report(
1497    surface_root: &Value,
1498    form_root: &Value,
1499    state_root: &Value,
1500    context_id: i64,
1501    tenant_id: i64,
1502    candidate_json: &str,
1503    current_id_text: &str,
1504) -> Option<String> {
1505    let candidate = serde_json::from_str::<Value>(if candidate_json.is_empty() {
1506        "{}"
1507    } else {
1508        candidate_json
1509    })
1510    .ok()?;
1511    let errors = if candidate.is_object() {
1512        Vec::<Value>::new()
1513    } else {
1514        vec![serde_json::json!({"code":"invalid_object","message":"candidate must be an object"})]
1515    };
1516    serde_json::to_string(&serde_json::json!({"schema":"pharos.validation.report.v1","surface_id":surface_root.get("surface_id"),"form_id":form_root.get("form_id"),"context_id":context_id,"tenant_id":tenant_id,"current_id":current_id_text,"state_present":!state_root.is_null(),"field_errors":errors})).ok()
1517}