execute_evcxr/
evcxr_source.rs

1use std::{
2    hash::{Hash, Hasher},
3    ops::Deref,
4    str::from_utf8_unchecked,
5};
6use syn::parse_str;
7
8use crate::{ParsedEvcxr, ScriptlikeRust};
9
10pub(crate) struct EvcxrSource<S>(pub S)
11where
12    S: AsRef<str>;
13
14impl<'a,S> From<S> for EvcxrSource<S>
15where
16    S: AsRef<str>
17{
18    fn from(s: S) -> Self {
19        EvcxrSource(s)
20    }
21}
22
23impl<'a, S> EvcxrSource<S>
24where
25    S: AsRef<str>
26{
27    pub(crate) fn parse(&self) -> syn::Result<ParsedEvcxr> {
28        let mut hasher = std::collections::hash_map::DefaultHasher::new();
29        self.0.as_ref().hash(&mut hasher);
30        let hash = hasher.finish();
31
32        let mut prefixed_dependencies = Vec::<&str>::with_capacity(255);
33        for dep in self
34            .0
35            .as_ref()
36            .lines()
37            .map(|line| line.trim())
38            .filter(|line| line.starts_with(":dep"))
39        {
40            prefixed_dependencies.push(dep);
41        }
42        let scriptlike_rust = match prefixed_dependencies.last().map(Deref::deref) {
43            None => self.0.as_ref(),
44            Some(line) => {
45                let src_ptr = self.0.as_ref().as_ptr();
46                let src_len = self.0.as_ref().len();
47                let line_ptr = line.as_ptr();
48                let line_len = line.len();
49                // TODO: use sub_ptr when available
50                // https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.sub_ptr
51                let src_to_line_offset = unsafe { line_ptr.offset_from(src_ptr) };
52                debug_assert!(src_to_line_offset >= 0);
53                let scriptlike_rust_ptr =
54                    unsafe { src_ptr.offset(src_to_line_offset + line_len as isize) };
55                let scriptlike_rust_len = src_len - src_to_line_offset as usize - line_len;
56                unsafe {
57                    from_utf8_unchecked(std::slice::from_raw_parts(
58                        scriptlike_rust_ptr,
59                        scriptlike_rust_len,
60                    ))
61                }
62                .trim()
63            }
64        };
65        let scriptlike_rust: ScriptlikeRust = parse_str(scriptlike_rust)?;
66        Ok(ParsedEvcxr {
67            prefixed_dependencies,
68            scriptlike_rust,
69            hash,
70        })
71    }
72}