1use core::fmt;
2
3use crate::{
4    entity::{EntityProjection, EntityProjectionMut},
5    Block, BlockRef, EntityCursor, EntityCursorMut, EntityMut, EntityRef, Operation, OperationRef,
6    Spanned,
7};
8
9#[derive(Default, Copy, Clone)]
19pub enum ProgramPoint {
20    #[default]
22    Invalid,
23    Block {
25        block: BlockRef,
27        position: Position,
29    },
30    Op {
32        op: OperationRef,
34        position: Position,
36    },
37}
38
39#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub enum Position {
42    Before,
44    After,
46}
47
48impl<T> From<EntityRef<'_, T>> for ProgramPoint
49where
50    for<'a> ProgramPoint: From<&'a T>,
51{
52    #[inline]
53    fn from(entity: EntityRef<'_, T>) -> Self {
54        Self::from(&*entity)
55    }
56}
57
58impl<T> From<EntityMut<'_, T>> for ProgramPoint
59where
60    for<'a> ProgramPoint: From<&'a T>,
61{
62    #[inline]
63    fn from(entity: EntityMut<'_, T>) -> Self {
64        Self::from(&*entity)
65    }
66}
67
68impl From<&Operation> for ProgramPoint {
70    #[inline]
71    fn from(op: &Operation) -> Self {
72        Self::from(op.as_operation_ref())
73    }
74}
75
76impl From<OperationRef> for ProgramPoint {
78    #[inline]
79    fn from(op: OperationRef) -> Self {
80        Self::Op {
81            op,
82            position: Position::Before,
83        }
84    }
85}
86
87impl From<&Block> for ProgramPoint {
89    #[inline]
90    fn from(block: &Block) -> Self {
91        Self::at_start_of(block.as_block_ref())
92    }
93}
94
95impl From<BlockRef> for ProgramPoint {
97    #[inline]
98    fn from(block: BlockRef) -> Self {
99        Self::Block {
100            block,
101            position: Position::Before,
102        }
103    }
104}
105
106#[doc(hidden)]
107#[derive(Copy, Clone)]
108pub struct BlockPoint {
109    block: BlockRef,
110    point: Position,
111}
112impl From<BlockPoint> for ProgramPoint {
113    fn from(point: BlockPoint) -> Self {
114        ProgramPoint::Block {
115            block: point.block,
116            position: point.point,
117        }
118    }
119}
120impl From<BlockRef> for BlockPoint {
121    fn from(block: BlockRef) -> Self {
122        Self {
123            block,
124            point: Position::Before,
125        }
126    }
127}
128impl From<&Block> for BlockPoint {
129    fn from(block: &Block) -> Self {
130        Self {
131            block: block.as_block_ref(),
132            point: Position::Before,
133        }
134    }
135}
136
137impl ProgramPoint {
138    #[inline]
140    pub fn before(entity: impl Into<ProgramPoint>) -> Self {
141        entity.into()
142    }
143
144    pub fn after(entity: impl Into<ProgramPoint>) -> Self {
146        let mut pp = entity.into();
147        match &mut pp {
148            Self::Invalid => (),
149            Self::Op {
150                position: ref mut point,
151                ..
152            }
153            | Self::Block {
154                position: ref mut point,
155                ..
156            } => {
157                *point = Position::After;
158            }
159        }
160        pp
161    }
162
163    pub fn at_start_of(block: impl Into<BlockPoint>) -> Self {
165        let BlockPoint { block, .. } = block.into();
166        Self::Block {
167            block,
168            position: Position::Before,
169        }
170    }
171
172    pub fn at_end_of(block: impl Into<BlockPoint>) -> Self {
174        let BlockPoint { block, .. } = block.into();
175        Self::Block {
176            block,
177            position: Position::After,
178        }
179    }
180
181    pub fn is_at_block_start(&self) -> bool {
183        self.operation().is_some_and(|op| {
184            op.parent().is_some() && op.prev().is_none() && self.placement() == Position::Before
185        }) || matches!(self, Self::Block { position: Position::Before, block, .. } if block.borrow().body().is_empty())
186    }
187
188    pub fn is_at_block_end(&self) -> bool {
190        self.operation().is_some_and(|op| {
191            op.parent().is_some() && op.next().is_none() && self.placement() == Position::After
192        }) || matches!(self, Self::Block { position: Position::After, block, .. } if block.borrow().body().is_empty())
193    }
194
195    pub fn block(&self) -> Option<BlockRef> {
199        match self {
200            Self::Invalid => None,
201            Self::Block { block, .. } => Some(*block),
202            Self::Op { op, .. } => op.parent(),
203        }
204    }
205
206    pub fn operation(&self) -> Option<OperationRef> {
210        match self {
211            Self::Invalid => None,
212            Self::Block {
213                position: Position::Before,
214                block,
215                ..
216            } => block.borrow().body().front().as_pointer(),
217            Self::Block {
218                position: Position::After,
219                block,
220                ..
221            } => block.borrow().body().back().as_pointer(),
222            Self::Op { op, .. } => Some(*op),
223        }
224    }
225
226    #[track_caller]
232    pub fn next_operation(&self) -> Option<OperationRef> {
233        assert!(!self.is_at_block_end());
234        match self {
235            Self::Op {
236                position: Position::After,
237                op,
238                ..
239            } if op.parent().is_some() => op.next(),
240            Self::Op { op, .. } => Some(*op),
241            Self::Block {
242                position: Position::Before,
243                block,
244            } => block.borrow().front(),
245            Self::Block { .. } | Self::Invalid => None,
246        }
247    }
248
249    #[track_caller]
255    pub fn prev_operation(&self) -> Option<OperationRef> {
256        assert!(!self.is_at_block_start());
257        match self {
258            Self::Op {
259                position: Position::Before,
260                op,
261                ..
262            } if op.parent().is_some() => op.prev(),
263            Self::Op { op, .. } => Some(*op),
264            Self::Block {
265                position: Position::After,
266                block,
267            } => block.borrow().back(),
268            Self::Block { .. } | Self::Invalid => None,
269        }
270    }
271
272    #[inline]
274    pub fn is_valid(&self) -> bool {
275        !self.is_unset()
276    }
277
278    #[inline]
280    pub fn is_unset(&self) -> bool {
281        matches!(self, Self::Invalid)
282    }
283
284    pub fn placement(&self) -> Position {
286        match self {
287            Self::Invalid => Position::After,
288            Self::Block {
289                position: point, ..
290            }
291            | Self::Op {
292                position: point, ..
293            } => *point,
294        }
295    }
296
297    pub fn cursor<'a, 'b: 'a, 'c: 'b>(
319        &'c self,
320    ) -> Option<EntityProjection<'b, EntityCursor<'a, Operation>>> {
321        match self {
322            Self::Invalid => None,
323            Self::Block {
324                block,
325                position: point,
326            } => Some(EntityRef::project(block.borrow(), |block| match point {
327                Position::Before => block.body().front(),
328                Position::After => block.body().back(),
329            })),
330            Self::Op {
331                op,
332                position: point,
333            } => {
334                let block = op.parent()?;
335                Some(EntityRef::project(block.borrow(), |block| match point {
336                    Position::Before => {
337                        if let Some(placement) = op.prev() {
338                            unsafe { block.body().cursor_from_ptr(placement) }
339                        } else {
340                            block.body().cursor()
341                        }
342                    }
343                    Position::After => unsafe { block.body().cursor_from_ptr(*op) },
344                }))
345            }
346        }
347    }
348
349    pub fn cursor_mut<'a, 'b: 'a, 'c: 'b>(
354        &'c mut self,
355    ) -> Option<EntityProjectionMut<'b, EntityCursorMut<'a, Operation>>> {
356        match self {
357            Self::Invalid => None,
358            Self::Block {
359                block,
360                position: point,
361            } => Some(EntityMut::project(block.borrow_mut(), |block| match point {
362                Position::Before => block.body_mut().cursor_mut(),
363                Position::After => block.body_mut().back_mut(),
364            })),
365            Self::Op {
366                op,
367                position: point,
368            } => {
369                let mut block = op.parent()?;
370                Some(EntityMut::project(block.borrow_mut(), |block| match point {
371                    Position::Before => {
372                        if let Some(placement) = op.prev() {
373                            unsafe { block.body_mut().cursor_mut_from_ptr(placement) }
374                        } else {
375                            block.body_mut().cursor_mut()
376                        }
377                    }
378                    Position::After => unsafe { block.body_mut().cursor_mut_from_ptr(*op) },
379                }))
380            }
381        }
382    }
383}
384
385impl Eq for ProgramPoint {}
386
387impl PartialEq for ProgramPoint {
388    fn eq(&self, other: &Self) -> bool {
389        match (self, other) {
390            (Self::Invalid, Self::Invalid) => true,
391            (Self::Invalid, _) | (_, Self::Invalid) => false,
392            (
393                Self::Block {
394                    block: x,
395                    position: xp,
396                },
397                Self::Block {
398                    block: y,
399                    position: yp,
400                },
401            ) => x == y && xp == yp,
402            (
403                Self::Op {
404                    op: x,
405                    position: xp,
406                    ..
407                },
408                Self::Op {
409                    op: y,
410                    position: yp,
411                    ..
412                },
413            ) => x == y && xp == yp,
414            (..) => false,
415        }
416    }
417}
418
419impl core::hash::Hash for ProgramPoint {
420    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
421        core::mem::discriminant(self).hash(state);
422        match self {
423            Self::Invalid => (),
424            Self::Block {
425                block,
426                position: point,
427            } => {
428                core::ptr::hash(BlockRef::as_ptr(block), state);
429                point.hash(state);
430            }
431            Self::Op {
432                op,
433                position: point,
434                ..
435            } => {
436                core::ptr::hash(OperationRef::as_ptr(op), state);
437                point.hash(state);
438            }
439        }
440    }
441}
442
443impl Spanned for ProgramPoint {
444    fn span(&self) -> crate::SourceSpan {
445        use crate::SourceSpan;
446
447        match self {
448            Self::Invalid => SourceSpan::UNKNOWN,
449            Self::Block {
450                block,
451                position: point,
452            } => match point {
453                Position::Before => {
454                    block.borrow().body().front().get().map(|op| op.span()).unwrap_or_default()
455                }
456                Position::After => {
457                    block.borrow().body().back().get().map(|op| op.span()).unwrap_or_default()
458                }
459            },
460            Self::Op { op, .. } => op.borrow().span(),
461        }
462    }
463}
464
465impl fmt::Display for ProgramPoint {
466    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
467        use crate::EntityWithId;
468        match self {
469            Self::Invalid => f.write_str("<invalid>"),
470            Self::Block {
471                block,
472                position: point,
473            } => match point {
474                Position::Before => write!(f, "start({})", &block.borrow().id()),
475                Position::After => write!(f, "end({})", &block.borrow().id()),
476            },
477            Self::Op {
478                op,
479                position: point,
480            } => {
481                use crate::formatter::{const_text, display};
482                let block = op
483                    .parent()
484                    .map(|blk| display(blk.borrow().id()))
485                    .unwrap_or_else(|| const_text("null"));
486                match point {
487                    Position::Before => {
488                        write!(f, "before({} in {block})", &op.borrow().name())
489                    }
490                    Position::After => {
491                        write!(f, "after({} in {block})", &op.borrow().name())
492                    }
493                }
494            }
495        }
496    }
497}
498
499impl fmt::Debug for ProgramPoint {
500    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
501        use crate::EntityWithId;
502        match self {
503            Self::Invalid => f.write_str("Invalid"),
504            Self::Block {
505                block,
506                position: point,
507            } => f
508                .debug_struct("Block")
509                .field("block", &block.borrow().id())
510                .field("point", point)
511                .finish(),
512            Self::Op {
513                op,
514                position: point,
515            } => f
516                .debug_struct("Op")
517                .field("block", &op.parent().map(|blk| blk.borrow().id()))
518                .field("point", point)
519                .field("op", &op.borrow())
520                .finish(),
521        }
522    }
523}