1use std::f64;
4use std::fmt;
5use std::hash::{Hash, Hasher};
6use std::i64;
7use std::str::FromStr;
8
9use uuid::UUID;
10
11use crate::{scan_for_float, scan_for_integer, scan_for_string};
12
13#[derive(Clone)]
16pub enum Atom {
17 UUID(UUID),
19 Integer(i64),
21 Float(f64),
23 String(String),
25}
26
27impl Hash for Atom {
28 fn hash<H: Hasher>(&self, state: &mut H) {
29 match self {
30 &Atom::String(ref s) => s.hash(state),
31 &Atom::Integer(ref s) => s.hash(state),
32 &Atom::Float(ref s) => format!("{}", s).hash(state),
33 &Atom::UUID(ref s) => s.hash(state),
34 }
35 }
36}
37
38impl PartialEq for Atom {
39 fn eq(&self, other: &Atom) -> bool {
40 match (self, other) {
41 (&Atom::String(ref a), &Atom::String(ref b)) => a == b,
42 (&Atom::Integer(ref a), &Atom::Integer(ref b)) => a == b,
43 (&Atom::UUID(ref a), &Atom::UUID(ref b)) => a == b,
44 (&Atom::Float(ref a), &Atom::Float(ref b)) => a - b < 0.0001,
45 _ => false,
46 }
47 }
48}
49
50impl Eq for Atom {}
51
52impl fmt::Debug for Atom {
53 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54 write!(f, "{}", self)
55 }
56}
57
58impl fmt::Display for Atom {
59 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60 match self {
61 &Atom::String(ref s) => {
62 let s = format!("{:?}", s);
63 write!(f, "'{}'", &s[1..s.len() - 1])
64 }
65 &Atom::Integer(ref s) => write!(f, "={}", s),
66 &Atom::Float(ref s) => write!(f, "^{}", s),
67 &Atom::UUID(ref s) => write!(f, ">{}", s.to_string()),
68 }
69 }
70}
71
72impl Atom {
73 pub fn is_uuid(&self) -> bool {
75 match self {
76 &Atom::UUID(_) => true,
77 _ => false,
78 }
79 }
80
81 pub fn is_integer(&self) -> bool {
83 match self {
84 &Atom::Integer(_) => true,
85 _ => false,
86 }
87 }
88
89 pub fn is_float(&self) -> bool {
91 match self {
92 &Atom::Float(_) => true,
93 _ => false,
94 }
95 }
96
97 pub fn is_string(&self) -> bool {
99 match self {
100 &Atom::String(_) => true,
101 _ => false,
102 }
103 }
104
105 pub fn parse<'a>(
108 input: &'a str, context: Option<(&UUID, &UUID)>,
109 ) -> Option<(Self, &'a str)> {
110 let input = input.trim_start();
111 match input.chars().next() {
112 Some('\'') => Self::parse_string(&input[1..]),
113 Some('=') => Self::parse_integer(&input[1..]),
114 Some('^') => Self::parse_float(&input[1..]),
115 Some('>') => {
116 UUID::parse(&input[1..], context)
117 .map(|(uu, cdr)| (Atom::UUID(uu), cdr))
118 }
119 _ => None,
120 }
121 }
122
123 fn parse_integer<'a>(input: &'a str) -> Option<(Self, &'a str)> {
124 let p = scan_for_integer(input).unwrap_or(input.len());
125 if p == 0 {
126 None
127 } else {
128 let (car, cdr) = input.split_at(p);
129 i64::from_str(car).ok().map(|i| (Atom::Integer(i), cdr))
130 }
131 }
132
133 fn parse_float<'a>(input: &'a str) -> Option<(Self, &'a str)> {
134 let p = scan_for_float(input).unwrap_or(input.len());
135 if p == 0 {
136 None
137 } else {
138 let (car, cdr) = input.split_at(p);
139 f64::from_str(car).ok().map(|i| (Atom::Float(i), cdr))
140 }
141 }
142
143 fn parse_string<'a>(input: &'a str) -> Option<(Self, &'a str)> {
144 scan_for_string(input).map(|off| {
145 let (a, b) = input.split_at(off);
146 (Atom::String(a.to_string()), &b[1..])
147 })
148 }
149}
150
151#[test]
152fn atom_uuid() {
153 let atom = Atom::UUID(UUID::Name { name: 0, scope: 0 });
154 assert_eq!(atom.is_uuid(), true);
155 assert_eq!(atom.is_integer(), false);
156 assert_eq!(atom.is_float(), false);
157 assert_eq!(atom.is_string(), false);
158 assert_eq!(&format!("{}", atom), ">0");
159}
160
161#[test]
162fn atom_integer() {
163 let atom = Atom::Integer(42);
164 assert_eq!(atom.is_uuid(), false);
165 assert_eq!(atom.is_integer(), true);
166 assert_eq!(atom.is_float(), false);
167 assert_eq!(atom.is_string(), false);
168 assert_eq!(&format!("{}", atom), "=42");
169}
170
171#[test]
172fn atom_float() {
173 let atom = Atom::Float(3.14);
174 assert_eq!(atom.is_uuid(), false);
175 assert_eq!(atom.is_integer(), false);
176 assert_eq!(atom.is_float(), true);
177 assert_eq!(atom.is_string(), false);
178 assert_eq!(&format!("{}", atom), "^3.14");
179}
180
181#[test]
182fn atom_string() {
183 let atom = Atom::String("atom".to_string());
184 assert_eq!(atom.is_uuid(), false);
185 assert_eq!(atom.is_integer(), false);
186 assert_eq!(atom.is_float(), false);
187 assert_eq!(atom.is_string(), true);
188 assert_eq!(&format!("{}", atom), "'atom'");
189}