vf_rs/lib.rs
1//! A set of ValueFlows structs and utils auto-generated from the [RDF schema][1].
2//! The structs are all serde-(de)serializable and are generically typed to
3//! allow a number of different methods of data modeling.
4//!
5//! The schema imports a number of structs from different RDF schemas, and each
6//! of them is namespaced in this crate, with the main classes generated living
7//! under `vf::*`.
8//!
9//! Given the nature of this library, it's important that various use cases are
10//! possible. For instance, you might want to model a VF system with all types
11//! tightly linked to each other as structs. You might want to loosely link the
12//! objects together with IDs (such as when storing in a normalized database).
13//! This means that the VF structs exported have generics for any references to
14//! other VF objects. This allows using whatever types are desired when building
15//! out your desired system.
16//!
17//! The structs exported have builder structs defined for them using the
18//! wonderful [derive_builder][2] crate. So for example `Agent` also has a corresponding
19//! `AgentBuilder` struct. Builder structs use the "owned" pattern, meaning the
20//! builder methods consume the builder and return a new instance on each call.
21//! The best way to create builders is by using the built-in `builder()`
22//! function for each type (ie, `Agent::builder()`). Also,
23//! given an existing `Agent` struct instance, you can call
24//! `myagent.into_builder()` to convert (consume) it into an `AgentBuilder`,
25//! which makes immutable updates fairly easy. The builder methods implement
26//! Into so any type that has Into implemented for the field type can be
27//! passed. Note that builder methods also strip Option, so if a struct field
28//! is an `Option<T>` you can just pass T to the builder method.
29//!
30//! This library defines getters and setters for the provided structs via the
31//! [getset][3] crate. It's important to note that by default, only getters are
32//! defined. If you want setters, you can compiled with the feature
33//! `getset_setters` and if you want mutable getters, use `getset_getmut`. This
34//! allows side-stepping some of the enforced functional nature of the library
35//! if you find that sort of thing obnoxious.
36//!
37//! Features:
38//!
39//! - `into_builder` - (default) implements `.into_builder()` for provided
40//! structs so existing structs can be modified via the builder pattern.
41//! - `getset_setters` - implements setters on the generated structs so they can
42//! be mutated in-place via setter methods
43//! - `getset_getmut` - implements mutable getters on the generated structs so
44//! they can be mutated in-place via &mut getters
45//!
46//! Note that *all* features are enabled when building the docs to give a sense
47//! of the library's full abilities.
48//!
49//! ```rust
50//! use vf_rs::vf;
51//!
52//! // build a new agent with the builder pattern, using String for the id field type
53//! let agent: vf::Agent = vf::Agent::builder()
54//! .name("Andrew")
55//! .note(Some("His hands are big".into()))
56//! .build().unwrap();
57//! assert_eq!(agent.name(), "Andrew");
58//! assert_eq!(agent.note(), &Some("His hands are big".into()));
59//! assert_eq!(agent.image(), &None);
60//! // create a new agent with a different label
61//! let new_agent = agent.into_builder()
62//! .note(Some("DOES NOT HAVE SMALL HANDS".into()))
63//! .build().unwrap();
64//! assert_eq!(new_agent.name(), "Andrew");
65//! assert_eq!(new_agent.note(), &Some("DOES NOT HAVE SMALL HANDS".into()));
66//! ```
67//!
68//! Note that this library contains absolutely no ValueFlows logic and exists
69//! solely as a definition for VF types.
70//!
71//! [1]: https://github.com/valueflows/valueflows/blob/master/release-doc-in-process/all_vf.TTL
72//! [2]: https://colin-kiegel.github.io/rust-derive-builder/
73//! [3]: https://docs.rs/getset/
74
75mod gen;
76
77// import everything lol
78pub use gen::*;
79
80#[cfg(test)]
81mod test {
82 use super::*;
83 use serde_json;
84 use url::Url;
85 use chrono::prelude::*;
86
87 #[test]
88 fn builder() {
89 let agent: vf::Agent = vf::Agent::builder()
90 .name("Andrew")
91 .note(Some("His hands are big".into()))
92 .build().unwrap();
93 assert_eq!(agent.name(), "Andrew");
94 assert_eq!(agent.note(), &Some("His hands are big".to_string()));
95 assert_eq!(agent.image(), &None);
96 }
97
98 #[cfg(feature = "into_builder")]
99 #[test]
100 fn into_builder() {
101 let agent: vf::Agent = vf::Agent::builder()
102 .name("Andrew")
103 .note(Some("His hands are big".into()))
104 .build().unwrap();
105 let agent_builder = agent.clone().into_builder();
106 let agent2 = agent_builder.build().unwrap();
107 assert_eq!(agent, agent2);
108
109 let agent3 = agent.clone().into_builder()
110 .name("LARRY".to_string())
111 .build().unwrap();
112 assert!(agent2 != agent3);
113 }
114
115 #[test]
116 fn builder_throws_on_incomplete_struct() {
117 let res: Result<vf::EconomicResource<Url, String, String, String, String>, String> = vf::EconomicResource::builder()
118 .name(Some("hi my name is butch".into()))
119 .build();
120 match res {
121 Ok(_) => panic!("Builder did not throw on missing required field"),
122 Err(_) => {}
123 }
124 }
125
126 #[test]
127 fn builder_setter_into() {
128 let agent: vf::Agent = vf::Agent::builder()
129 .name("Andrew".to_string())
130 .build().unwrap();
131 assert_eq!(agent.name(), "Andrew");
132 let agent: vf::Agent = vf::Agent::builder()
133 .name("Andrew")
134 .build().unwrap();
135 assert_eq!(agent.name(), "Andrew");
136 }
137
138 #[cfg(feature = "with_serde")]
139 #[test]
140 fn serializes() {
141 let location = geo::SpatialThing::builder()
142 .name(Some("https://basisproject.gitlab.io/public/".into()))
143 .build().unwrap();
144 let agent: vf::Agent = vf::Agent::builder()
145 .image("https://basisproject.gitlab.io/public/assets/images/red_star.256.outline.png".parse::<Url>().unwrap())
146 .name("Basis")
147 .primary_location(location)
148 .build().unwrap();
149 let json = serde_json::to_string(&agent).unwrap();
150 assert_eq!(json, r#"{"image":"https://basisproject.gitlab.io/public/assets/images/red_star.256.outline.png","name":"Basis","primary_location":{"name":"https://basisproject.gitlab.io/public/"}}"#);
151 }
152
153 #[cfg(feature = "with_serde")]
154 #[test]
155 fn deserializes() {
156 let json = r#"{"image":"https://basisproject.gitlab.io/public/assets/images/red_star.256.outline.png","name":"Basis","primary_location":{"name":"https://basisproject.gitlab.io/public/"}}"#;
157 let agent: vf::Agent = serde_json::from_str(json).unwrap();
158 let location = agent.primary_location().as_ref().unwrap();
159 assert_eq!(agent.image(), &Some("https://basisproject.gitlab.io/public/assets/images/red_star.256.outline.png".parse::<Url>().unwrap()));
160 assert_eq!(agent.name(), "Basis");
161 assert_eq!(agent.note(), &None);
162 assert_eq!(location.name(), &Some("https://basisproject.gitlab.io/public/".into()));
163 }
164
165 #[cfg(feature = "getset_setters")]
166 #[test]
167 fn getset_setters() {
168 let mut plan = vf::Plan::builder()
169 .created("2018-04-01T00:01:01Z".parse::<DateTime<Utc>>().unwrap())
170 .name("GOSHPLAN".to_string())
171 .build().unwrap();
172 assert_eq!(plan.name(), &Some("GOSHPLAN".to_string()));
173 plan.set_name(Some("Gffft".into()));
174 assert_eq!(plan.name(), &Some("Gffft".to_string()));
175 }
176
177 #[cfg(feature = "getset_getmut")]
178 #[test]
179 fn getset_getmut() {
180 let mut plan = vf::Plan::builder()
181 .created("2018-04-01T00:01:01Z".parse::<DateTime<Utc>>().unwrap())
182 .name("GOSHPLAN".to_string())
183 .build().unwrap();
184 assert_eq!(plan.name(), &Some("GOSHPLAN".to_string()));
185 (*plan.name_mut()) = Some("Gffft".into());
186 assert_eq!(plan.name(), &Some("Gffft".to_string()));
187 }
188}
189