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
use crate::datatypes::RandomData;
use failure::Fail;
use std::iter::Iterator;
use std::string::ToString;

use serde_derive::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Reference {
	path: String,
	property: String,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "type", content = "value")]
pub enum DataType {
	RandomData(RandomData),
	List(Box<DataType>),
	Model(String),
	Reference { path: String, property: String },
}

use std::collections::{hash_map::Iter, HashMap};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Model {
	#[serde(flatten)]
	properties: HashMap<String, DataType>,
}

impl Model {
	pub fn type_iter(&self) -> Iter<String, DataType> {
		self.properties.iter()
	}
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Specification {
	serialize: HashMap<String, Vec<String>>,
	models: HashMap<String, Model>,
}

impl Specification {
	pub fn has_model<S: ToString>(&self, name: S) -> bool {
		self.models.contains_key(&name.to_string())
	}
	pub fn get_definition<S: ToString>(&self, name: S) -> &Model {
		&self.models.get(&name.to_string()).unwrap()
	}
	pub fn get_serialize_ref<S: ToString>(&self, name: S) -> Option<&Vec<String>> {
		self.serialize.get(&name.to_string())
	}
}

pub mod io {
	use super::Specification;
	use crate::generator::from_spec;

	use failure::Fail;
	use std::convert::AsRef;
	use std::fs::read_to_string;
	use std::io::Result;
	use std::path::{Path, PathBuf};

	use serde_derive::{Deserialize, Serialize};
	use serde_json::{error::Category, from_str};

	pub type SpecResult<Success> = std::result::Result<Success, SpecError>;

	#[derive(Debug, Fail)]
	pub enum SpecError {
		#[fail(display = "Could not find the spec file: {}", 0)]
		MissingFile(String),
		#[fail(display = "Failed for unhandled reason: {}", inner)]
		IOError { inner: std::io::Error },
		#[fail(display = "The provided spec path could not be correctly converted")]
		BadPath,
		#[fail(display = "The specification is not valid JSON")]
		BadFormat,
		#[fail(display = "The specification contained one or more invalid definitions")]
		BadData,
	}

	fn pathable_to_string<P: AsRef<Path>>(path: &P) -> SpecResult<String> {
		return match path.as_ref().to_str() {
			Some(string) => Ok(String::from(string)),
			None => Err(SpecError::BadPath),
		};
	}

	pub fn read_spec<P: AsRef<Path>>(path: P) -> SpecResult<Specification> {
		let content = match read_to_string(&path) {
			Ok(content) => content,
			Err(e) => match e.kind() {
				std::io::ErrorKind::NotFound => {
					return Err(SpecError::MissingFile(pathable_to_string(&path)?))
				}
				_ => return Err(SpecError::IOError { inner: e }),
			},
		};

		match from_str(&content) {
			Ok(spec) => Ok(spec),
			Err(e) => match e.classify() {
				Category::Eof | Category::Syntax => Err(SpecError::BadFormat),
				Category::Data => Err(SpecError::BadData),
				Category::Io => Err(SpecError::IOError { inner: e.into() }),
			},
		}
	}
}