stof/toml/
mod.rs

1//
2// Copyright 2024 Formata, Inc. All rights reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//    http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17pub mod import;
18use import::parse_object_value;
19
20pub mod export;
21use export::toml_value_from_node;
22use toml::{Table, Value};
23
24use crate::{lang::SError, Format, IntoNodeRef, SDoc, SGraph};
25
26
27/// Stof TOML interface.
28pub struct TOML;
29impl TOML {
30    /// Parse a TOML string into a new document.
31    pub fn parse_new(toml: &str) -> Result<SDoc, SError> {
32        Ok(SDoc::new(Self::parse(toml)?))
33    }
34
35    /// Parse a TOML string into a Stof graph.
36    pub fn parse(toml: &str) -> Result<SGraph, SError> {
37        if let Ok(value) = toml.parse::<Table>() {
38            let mut graph = SGraph::default();
39            let root = graph.insert_root("root");
40            parse_object_value(&mut graph, &root, Table::from(value));
41            Ok(graph)
42        } else {
43            Err(SError::empty_fmt("toml", "unable to parse TOML string into a table"))
44        }
45    }
46
47    /// Export a graph as a TOML string.
48    /// Exports the main root of the graph only.
49    pub fn stringify(pid: &str, doc: &SDoc) -> Result<String, SError> {
50        if let Some(main) = doc.graph.main_root() {
51            let value = toml_value_from_node(&doc.graph, &main);
52            if let Ok(toml) = toml::to_string(&Value::Table(value)) {
53                return Ok(toml);
54            }
55            return Err(SError::fmt(pid, doc, "toml", "could not parse toml::Value into a toml string"));
56        }
57        Err(SError::fmt(pid, doc, "toml", "did not find a main root to stringify"))
58    }
59
60    /// Export a node as a TOML string.
61    pub fn stringify_node(pid: &str, doc: &SDoc, node: impl IntoNodeRef) -> Result<String, SError> {
62        let value = toml_value_from_node(&doc.graph, &node.node_ref());
63        if let Ok(toml) = toml::to_string(&Value::Table(value)) {
64            return Ok(toml);
65        }
66        Err(SError::fmt(pid, doc, "toml", "could not parse toml::Value into a toml string"))
67    }
68}
69
70impl Format for TOML {
71    /// Format getter for TOML.
72    fn format(&self) -> String {
73        "toml".to_string()
74    }
75
76    /// Content type for TOML.
77    fn content_type(&self) -> String {
78        "text/toml".to_string()
79    }
80
81    /// Header import.
82    fn header_import(&self, pid: &str, doc: &mut crate::SDoc, _content_type: &str, bytes: &mut bytes::Bytes, as_name: &str) -> Result<(), SError> {
83        let res = std::str::from_utf8(bytes.as_ref());
84        match res {
85            Ok(str) => {
86                self.string_import(pid, doc, str, as_name)
87            },
88            Err(error) => {
89                Err(SError::fmt(pid, &doc, "toml", &error.to_string()))
90            }
91        }
92    }
93
94    /// String import.
95    fn string_import(&self, pid: &str, doc: &mut crate::SDoc, src: &str, as_name: &str) -> Result<(), SError> {
96        let mut graph = TOML::parse(src)?;
97        if as_name.len() > 0 && as_name != "root" {
98            let mut path = as_name.replace(".", "/");
99            if as_name.starts_with("self") || as_name.starts_with("super") {
100                if let Some(ptr) = doc.self_ptr(pid) {
101                    path = format!("{}/{}", ptr.path(&doc.graph), path);
102                }
103            }
104
105            // as_name is really a location, so ensure the nodes and move it there
106            let mut loc_graph = SGraph::default();
107            let loc = loc_graph.ensure_nodes(&path, '/', true, None);
108            if let Some(main) = graph.main_root() {
109                if let Some(main) = main.node(&graph) {
110                    loc_graph.absorb_external_node(&graph, main, &loc);
111                }
112            }
113            graph = loc_graph;
114        }
115        doc.graph.default_absorb_merge(graph)
116    }
117
118    /// File import.
119    fn file_import(&self, pid: &str, doc: &mut crate::SDoc, _format: &str, full_path: &str, _extension: &str, as_name: &str) -> Result<(), SError> {
120        let src = doc.fs_read_string(pid, full_path)?;
121        self.string_import(pid, doc, &src, as_name)
122    }
123
124    /// Export string.
125    fn export_string(&self, pid: &str, doc: &crate::SDoc, node: Option<&crate::SNodeRef>) -> Result<String, SError> {
126        if node.is_some() {
127            TOML::stringify_node(pid, &doc, node)
128        } else {
129            TOML::stringify(pid, &doc)
130        }
131    }
132}