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