clojure-reader 0.6.1

A Clojure reader
Documentation
#[cfg(feature = "unstable")]
mod test {
	use clojure_reader::{
		edn::{self, Edn},
		error::Code,
		parse::{self, Node, NodeKind, Position, SourceReader, Span},
	};

	#[test]
	fn node_try_into_edn_matches_read_string() {
		let input = r#"#_ :ignored {:foo [1 #_2 {:bar nil}] :baz #{true #_false :qux} :tagged #inst "1985-04-12T23:20:50.52Z"} trailing"#;

		let mut reader = SourceReader::new(input);
		let node = parse::parse(&mut reader).unwrap();
		let edn = Edn::try_from(node).unwrap();

		assert_eq!(edn, edn::read_string(input).unwrap());
		assert_eq!(reader.remaining(), " trailing");
	}

	#[test]
	fn node_try_into_edn_reports_duplicate_errors() {
		let map_node = parse::parse(&mut SourceReader::new("{:a 1 :a [2]}")).unwrap();
		let map_err = Edn::try_from(map_node).unwrap_err();
		assert_eq!(map_err.code, Code::HashMapDuplicateKey);
		assert_eq!(map_err.line, Some(1));
		assert_eq!(map_err.column, Some(13));
		assert_eq!(map_err.ptr, Some(12));

		let set_node = parse::parse(&mut SourceReader::new("#{:cat 1 2 [42] 2}")).unwrap();
		let set_err = Edn::try_from(set_node).unwrap_err();
		assert_eq!(set_err.code, Code::SetDuplicateKey);
		assert_eq!(set_err.line, Some(1));
		assert_eq!(set_err.column, Some(18));
		assert_eq!(set_err.ptr, Some(17));
	}

	#[test]
	fn node_try_into_edn_reports_invalid_tags() {
		let node = parse::parse(&mut SourceReader::new("#4 2")).unwrap();
		let err = Edn::try_from(node).unwrap_err();

		assert_eq!(err.code, Code::InvalidTag);
		assert_eq!(err.line, Some(1));
		assert_eq!(err.column, Some(2));
		assert_eq!(err.ptr, Some(1));

		for input in [
			"#foo/bar/baz nil",
			"#foo/ nil",
			"#foo//bar nil",
			"#foo/42 nil",
			"#foo/:bar nil",
			"#foo/#bar nil",
			"#foo/-42 nil",
			"#foo/+42 nil",
			"#foo/.42 nil",
			"#foo|bar nil",
			"#foo@bar nil",
			"#:foo 1",
		] {
			let node = parse::parse(&mut SourceReader::new(input)).unwrap();
			assert_eq!(Edn::try_from(node).unwrap_err().code, Code::InvalidTag);
		}
	}

	#[test]
	fn node_try_into_edn_covers_remaining_variants() {
		let node = Node::no_discards(
			NodeKind::List(
				vec![
					Node::no_discards(NodeKind::Symbol("sym"), Span::default()),
					Node::no_discards(NodeKind::Rational((3, 2)), Span::default()),
					Node::no_discards(NodeKind::Char('z'), Span::default()),
				],
				vec![],
			),
			Span::default(),
		);
		assert_eq!(
			Edn::try_from(node).unwrap(),
			Edn::List(vec![Edn::Symbol("sym"), Edn::Rational((3, 2)), Edn::Char('z')])
		);

		#[cfg(feature = "floats")]
		assert_eq!(
			Edn::try_from(Node::no_discards(NodeKind::Double((2.5).into()), Span::default())).unwrap(),
			Edn::Double((2.5).into())
		);

		#[cfg(feature = "arbitrary-nums")]
		{
			use bigdecimal::BigDecimal;
			use num_bigint::BigInt;

			assert_eq!(
				Edn::try_from(Node::no_discards(NodeKind::BigInt(BigInt::from(42)), Span::default()))
					.unwrap(),
				Edn::BigInt(BigInt::from(42))
			);
			assert_eq!(
				Edn::try_from(Node::no_discards(NodeKind::BigDec(BigDecimal::from(42)), Span::default(),))
					.unwrap(),
				Edn::BigDec(BigDecimal::from(42))
			);
		}
	}

	#[test]
	fn parse_rejects_maps_with_odd_elements() {
		let err = parse::parse(&mut SourceReader::new("{:a}")).unwrap_err();

		assert_eq!(err.code, Code::UnexpectedEOF);
		assert_eq!(err.line, Some(1));
		assert_eq!(err.column, Some(4));
		assert_eq!(err.ptr, Some(3));
	}

	#[test]
	fn source_reader_finish_keeps_source_and_position() {
		let mut reader = SourceReader::new("(cat) [42]");
		let _ = parse::parse(&mut reader).unwrap();

		let (position, source) = reader.finish();

		assert_eq!(position, Position { line: 1, column: 6, ptr: 5 });
		assert_eq!(source, "(cat) [42]");
		assert_eq!(&source[position.ptr..], " [42]");
	}
}