1use std::{
4 fmt,
5 marker::PhantomData,
6 ops::Bound,
7 os::raw::c_int,
8};
9use crate::{
10 prelude::*,
11 object::NonNullObject,
12 ruby,
13};
14
15mod into_bounds;
16pub use into_bounds::*;
17
18#[repr(transparent)]
62pub struct Range<S = AnyObject, E = S> {
63 inner: NonNullObject,
64 _marker: PhantomData<(S, E)>,
65}
66
67impl<S, E> Clone for Range<S, E> {
68 fn clone(&self) -> Self { *self }
69}
70
71impl<S, E> Copy for Range<S, E> {}
72
73impl<S: Object, E: Object> AsRef<AnyObject> for Range<S, E> {
74 #[inline]
75 fn as_ref(&self) -> &AnyObject { self.inner.as_ref() }
76}
77
78impl<S: Object, E: Object> From<Range<S, E>> for AnyObject {
79 #[inline]
80 fn from(object: Range<S, E>) -> AnyObject { object.inner.into() }
81}
82
83impl<S: Object, E: Object> PartialEq<AnyObject> for Range<S, E> {
84 #[inline]
85 fn eq(&self, obj: &AnyObject) -> bool {
86 self.as_any_object() == obj
87 }
88}
89
90impl<S: Object, E: Object> fmt::Debug for Range<S, E> {
91 #[inline]
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 f.debug_tuple("Range")
94 .field(&self.inner)
95 .finish()
96 }
97}
98
99unsafe impl<S: Object, E: Object> Object for Range<S, E> {
100}
101
102impl<S: Object, E: Object> IntoBounds<S, E> for Range<S, E> {
103 #[inline]
104 fn into_bounds(self) -> (S, Bound<E>) {
105 unsafe {
106 let mut start: ruby::VALUE = 0;
107 let mut end: ruby::VALUE = 0;
108 let mut excl: c_int = 0;
109 ruby::rb_range_values(self.raw(), &mut start, &mut end, &mut excl);
110
111 let start = S::from_raw(start);
112
113 let end = if end == crate::util::NIL_VALUE {
114 Bound::Unbounded
115 } else {
116 let end = E::from_raw(end);
117 if excl != 0 {
118 Bound::Excluded(end)
119 } else {
120 Bound::Included(end)
121 }
122 };
123
124 (start, end)
125 }
126 }
127}
128
129impl Range {
130 pub fn from_bounds(
135 start: AnyObject,
136 end: AnyObject,
137 exclusive: bool,
138 ) -> Result<Self> {
139 unsafe {
140 crate::protected_no_panic(|| {
141 Self::from_bounds_unchecked(start, end, exclusive)
142 })
143 }
144 }
145
146 #[inline]
155 pub unsafe fn from_bounds_unchecked(
156 start: AnyObject,
157 end: AnyObject,
158 exclusive: bool,
159 ) -> Self {
160 let raw = ruby::rb_range_new(start.raw(), end.raw(), exclusive as _);
161 Self::from_raw(raw)
162 }
163}
164
165impl<S: Object, E: Object> Range<S, E> {
166 #[inline]
169 pub fn new<R, A, B>(range: R) -> Result<Self>
170 where
171 R: IntoBounds<A, B>,
172 A: Into<S>,
173 B: Into<E>,
174 {
175 let (start, end) = range.into_bounds();
176 let start = start.into().into_any_object();
177 let (end, exclusive) = match end {
178 Bound::Included(end) => (end.into().into_any_object(), false),
179 Bound::Excluded(end) => (end.into().into_any_object(), true),
180 Bound::Unbounded => (AnyObject::nil(), true),
181 };
182 unsafe {
183 let range = Range::from_bounds(start, end, exclusive)?;
184 Ok(Self::cast_unchecked(range))
185 }
186 }
187
188 #[inline]
194 pub unsafe fn new_unchecked<R, A, B>(range: R) -> Self
195 where
196 R: IntoBounds<A, B>,
197 A: Into<S>,
198 B: Into<E>,
199 {
200 let (start, end) = range.into_bounds();
201 let start = start.into().into_any_object();
202 let (end, exclusive) = match end {
203 Bound::Included(end) => (end.into().into_any_object(), false),
204 Bound::Excluded(end) => (end.into().into_any_object(), true),
205 Bound::Unbounded => (AnyObject::nil(), true),
206 };
207 let range = Range::from_bounds_unchecked(start, end, exclusive);
208 Self::cast_unchecked(range)
209 }
210
211 #[inline]
213 pub fn into_any_range(self) -> Range {
214 unsafe { Range::cast_unchecked(self) }
215 }
216
217 #[inline]
219 pub fn into_bounds(self) -> (S, Bound<E>) {
220 IntoBounds::into_bounds(self)
221 }
222}