1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at https://mozilla.org/MPL/2.0/.

#![deny(missing_docs)]

//! Genere is a library to generate (possibly randomized) text with options to match the (grammatical) gender
//! of various elements.
//!
//! # Example
//!
//! ```
//! use genere::Generator;
//! let json = r#"
//! {
//!    "hero": ["John[m]", "Joan[f]"],
//!    "job[hero]": ["wizard/witch"],
//!    "main[hero]": ["{hero}. He/She is a {job}."]
//! }"#;
//!
//! let mut gen = Generator::new();
//! gen.add_json(json).unwrap();;
//! let result = gen.instantiate("main").unwrap();
//! assert!(&result == "John. He is a wizard."
//!        || &result == "Joan. She is a witch.");
//! ```
//! 
//!
//! # Features
//!
//! ## Binary or Rust library
//!
//! It is possible to use Genere as a binary:
//!
//! ```bash
//! $ genere main < file.json
//! ```
//! will instantiate the `main` symbol in the `file.json` file.
//!
//! Genere is, however, primarily a [Rust](https://rust-lang.org) library, so it can be used in programs written in Rust: you only have to add
//!
//! ```toml
//! genere = "0.1"
//! ```
//!
//! In the `dependencies` section of your `Cargo.toml` file.
//!
//! ## Text generation
//!
//! Genere is inspired by [Tracery](http://tracery.io/) and thus has a similar syntax to allow
//! you to easily generate randonized text:
//!
//! ```
//! # use genere::Generator;
//! let json = r#"
//! {
//!     "name": ["John", "Johana", "Vivienne", "Eric"],
//!     "last_name": ["StrongArm", "Slayer", "The Red"],
//!     "class": ["mage", "warrior", "thief", "rogue", "barbarian"],
//!     "race": ["human", "dwarvish", "elvish", "vampire"],
//!     "text": ["{name} {last_name} is a {race} {class}.",
//! 	     "Meet {name} {last_name}, A proud {class}!"]
//! }
//! "#;
//!
//! # let mut gen = Generator::new();
//! # gen.add_json(json).unwrap();;
//! # let result = gen.instantiate("text").unwrap();
//! # println!("{}", result);
//! ```
//!
//! might display "Johana  Slayer is a vampire warrior."
//!
//! Basically, you define a list of symbols which will be replaced (randomly) by one version
//! of the string in the corresponding array when you "call" them using the `{symbol`} syntax.
//!
//! Not that once a symbol has been "instantiated", ils value is fixed. So if you had:
//!
//! ```json
//! "text": ["Meet {name} {last_name}. {name} is a proud {class}."]
//! ```
//!
//! it is guaranteed that both replacements for `{name}` will be identical.
//!
//! If you want to get a (possibly) different instantiation, you need to use `{{symbol}}`:
//!
//! ```json
//! "text": ["Meet {name} {last_name}. {name} is a proud {class}. There is also {{name}}, a {{class}}."]
//! ```
//!
//! ## Capitalization
//!
//! When declared, symbols are case-insensitive. When they are referred to in content replacements,
//! the capitalization of the symbol will impact the capitalization of
//! the replacement: if thhe symbol is in lowercase, the content is
//! not touched; if only the first letter of the symbol is in
//! uppercase, the first letter of the replacement content will be
//! changed to uppercase; and if the symbol is all in uppercase, the
//! same will be applied for the replacement content.
//!
//! ```
//! # use genere::Generator;
//! let json = r#"
//! {
//!     "dog": ["a good dog"],
//!     "text1": ["This is {dog}"],
//!     "text2": ["This is {DOG}"],
//!     "text3": ["{Dog}"]
//! }
//! "#;
//!
//! # let mut gen = Generator::new();
//! # gen.add_json(json).unwrap();;
//! # let t1 = gen.instantiate("text1").unwrap();
//! # let t2 = gen.instantiate("text2").unwrap();
//! # let t3 = gen.instantiate("text3").unwrap();
//! # assert_eq!(&t1, "This is a good dog");
//! # assert_eq!(&t2, "This is A GOOD DOG");
//! # assert_eq!(&t3, "A good dog");
//! ```
//!
//! will display "This is a good dog", "This is A GOOD DOG" and "A good dog" for "text1", "text2" and "text3" respectively.
//!
//! ## Gender adaptation
//!
//! Genere seeks to allow easy generation of sentences that are grammaticaly gender accurate:
//!
//! ```
//! # use genere::Generator;
//! let json = r#"
//! {
//!     "name": ["John[m]", "Johana[f]", "Vivienne[f]", "Eric[m]"],
//!     "class": ["mage", "warrior", "thief", "rogue", "barbarian"],
//!     "text[name]": ["Meet {name}. He/She is a proud {class}!"]
//! }
//! "#;
//!
//! # let mut gen = Generator::new();
//! # gen.add_json(json).unwrap();;
//! # let result = gen.instantiate("text").unwrap();
//! # println!("{}", result);
//! ```
//!
//! will make sure to display "He" or She" according to the gender specified in the symbol `name`.
//!
//! You can set a gender to these values using the `[m]`, `[f]` or `[n]`. Similarly, you can
//! tell genere that a symbol depends on another's symbol gender by using `[symbol]` in the symbol name. E.g., `text[main]` means that the gender in `main`'s replacement strings will be determined by `name`'s gender.

//! It is also possible to specify a neutral gender, by using `[n]` in the definition and by
//! adding a `/` in the replacement string (e.g. `He/She/They`). If it isn't specified in the
//! replacement string, both male and female version will be outputted (e.g. `He/She` instead of `Them`).
//!
//! Sometimes a sentence might use various gendered elements and not just depend on only one symbol's gender.
//! For each gender variation, it is possible to specify a "dependency":
//!
//! ```json
//! "text[hero]": ["He/She is called {hero}. His/Her son/daughter[child] is named {child}."]
//! ```
//!
//! Here, the gender of `hero` will be used to determine between `He/She` and `His/Her`, but
//! the gender of `child` will be used to pick between `son/daughter`.
//!
//! ### Spaces in gender adaptation
//!
//! When you use this gender syntax, the '/' will only consider the word before and the word
//! after, not allowing to have spaces in your expressions. If you want to insert a space in a
//! gender adaptation expression, you must escape it with `~`, e.g.: "du/de~ la"
//!
//! ## Additional gender syntax
//!
//! It is also possible to use the "median point" syntax used e.g. in french: "C'est un·e sorci·er·ère." is equivalent to "C'est un/une sorcier/sorcière".
//!
//! ## Escaping
//!
//! If you want to use the '[', ']', '{', '}', '/' and '·' characters in your text, you can use
//! the escape character '\~'. E.g., "\~{foo}" will display "{foo}" instead of trying to find the symbol `foo` and replace it with its content. You can also use "~~" if you want to display the tilde symbol.
//!
//! ## License
//!
//! Genere is published under the Mozilla Public License, version 2.0. For more information, see the [License](LICENSE).
//!
//! ## ChangeLog
//!
//! See [ChangeLog](ChangeLog.md).


mod errors;
mod generator;

pub use generator::Generator;
pub use generator::Gender;
pub use errors::Result;