Skip to main content

graphite_command/
types.rs

1use std::{alloc::Layout, result};
2
3use bytemuck::NoUninit;
4use bytes::BufMut;
5
6#[derive(Debug, Clone, Copy)]
7pub struct Span {
8    pub start: usize,
9    pub end: usize,
10}
11
12#[derive(Debug, Clone, Copy)]
13pub struct SpannedWord<'a> {
14    pub span: Span,
15    pub word: &'a str,
16}
17
18pub struct ParseState<'a> {
19    finalized: bool,
20    argument_layout: Layout,
21    arguments: Vec<u8>,
22    argument_spans: Vec<Span>,
23    pub(crate) words: Vec<SpannedWord<'a>>,
24    pub(crate) cursor: usize,
25    pub full_span: Span,
26}
27
28impl<'a> ParseState<'a> {
29    pub fn new(input: &'a str) -> Self {
30        let mut words = Vec::new();
31        let mut start = 0;
32        let mut end = 0;
33        let mut in_word = false;
34        for (index, char) in input.chars().enumerate() {
35            if char.is_whitespace() {
36                if in_word {
37                    words.push(SpannedWord {
38                        span: Span { start, end },
39                        word: &input[start..=end],
40                    })
41                }
42                in_word = false;
43            } else {
44                if !in_word {
45                    in_word = true;
46                    start = index;
47                }
48                end = index;
49            }
50        }
51        if in_word {
52            words.push(SpannedWord {
53                span: Span { start, end },
54                word: &input[start..=end],
55            })
56        }
57
58        Self {
59            finalized: false,
60            argument_layout: unsafe { Layout::from_size_align_unchecked(0, 1) },
61            arguments: Vec::new(),
62            argument_spans: Vec::new(),
63            words,
64            cursor: 0,
65            full_span: Span { start: 0, end },
66        }
67    }
68
69    pub(crate) fn get_arguments(&mut self) -> (&[u8], &[Span]) {
70        debug_assert!(!self.finalized);
71        self.finalized = true;
72
73        // Get size and align of layout
74        let len = self.argument_layout.size();
75        let align = self.argument_layout.align();
76        debug_assert_eq!(
77            len,
78            self.arguments.len(),
79            "layout length must match data length"
80        );
81
82        // Compute padding (code from Layout::padding_needed_for)
83        let padding = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
84        let padding = padding.wrapping_sub(len);
85
86        // Extend arguments with padding
87        self.arguments.resize(len + padding, 0);
88
89        (self.arguments.as_slice(), self.argument_spans.as_slice())
90    }
91
92    pub(crate) fn cursor(&self) -> usize {
93        self.cursor
94    }
95
96    #[allow(dead_code)] // todo: use this method
97    pub(crate) fn peek_input<'b>(&'b self) -> Option<SpannedWord<'a>> {
98        if self.is_finished() {
99            None
100        } else {
101            Some(self.words[self.cursor])
102        }
103    }
104
105    pub(crate) fn pop_input<'b>(&'b mut self) -> Option<SpannedWord<'a>> {
106        if self.is_finished() {
107            None
108        } else {
109            self.cursor += 1;
110            Some(self.words[self.cursor - 1])
111        }
112    }
113
114    pub(crate) fn is_finished(&self) -> bool {
115        self.cursor >= self.words.len()
116    }
117
118    #[allow(dead_code)] // todo: use this method
119    pub(crate) fn advance(&mut self, advance: usize) {
120        self.cursor += advance;
121        debug_assert!(self.cursor <= self.words.len());
122    }
123
124    pub fn push_str(&mut self, arg: &str, span: Span) {
125        let raw_slice: u128 = unsafe { std::mem::transmute(arg) };
126        self.push_arg(raw_slice, span);
127    }
128
129    pub fn push_ref<T>(&mut self, arg: &T, span: Span) {
130        let raw_reference: usize = unsafe { std::mem::transmute(arg) };
131        self.push_arg(raw_reference, span);
132    }
133
134    pub fn push_arg<T: NoUninit>(&mut self, arg: T, span: Span) {
135        // Get layout for argument
136        let arg_layout = Layout::new::<T>();
137
138        // Update layout
139        let (new_layout, offset) = self.argument_layout.extend(arg_layout).unwrap();
140        self.argument_layout = new_layout;
141        self.arguments.resize(offset, 0);
142
143        // Put bytes and span
144        let bytes: &[u8] = bytemuck::bytes_of(&arg);
145        debug_assert_eq!(arg_layout.size(), bytes.len());
146        self.arguments.put_slice(bytes);
147        self.argument_spans.push(span);
148    }
149}
150
151pub type DispatchFunction = fn(&[u8], &[Span]) -> CommandDispatchResult;
152
153pub type CommandResult = result::Result<(), String>;
154
155#[derive(Debug)]
156pub enum CommandParseResult {
157    Ok,
158    Err {
159        span: Span,
160        errmsg: String,
161        continue_parsing: bool,
162    },
163}
164
165#[derive(Debug)]
166pub enum CommandDispatchResult {
167    Success(CommandResult),
168    ParseError {
169        span: Span,
170        errmsg: String,
171        continue_parsing: bool,
172    },
173    UnknownCommand,
174    IncompleteCommand,
175    TooManyArguments,
176}