mtots_core 0.1.2

Core implementation of the mtots scripting language
Documentation
use super::*;

pub(super) fn new() -> Rc<Class> {
    Class::new(
        "String".into(),
        Class::map_from_funcs(vec![
            NativeFunction::new(
                "len",
                ["self"],
                concat!(
                    "Returns the number of bytes in the UTF-8 representation of ",
                    "this string\n\n",
                ),
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    Ok(Value::from(owner.len()))
                },
            ),
            NativeFunction::new(
                "__add",
                ["self", "other"],
                "Concatenates the two strings",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let mut owner = args.next().unwrap().into_string()?.unwrap_or_clone();
                    let other = args.next().unwrap().into_string()?;
                    owner.push_str(other.str());
                    Ok(Value::from(owner))
                },
            ),
            NativeFunction::new(
                "join",
                ["self", "parts"],
                "Returns a new string with the old pattern replaced with the new",
                |globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    let mut parts = args.next().unwrap().unpack(globals)?.into_iter();
                    let mut ret = String::new();
                    if let Some(part) = parts.next() {
                        ret.push_str(part.into_string()?.str());
                        while let Some(part) = parts.next() {
                            ret.push_str(owner.str());
                            ret.push_str(part.into_string()?.str());
                        }
                    }
                    Ok(Value::from(ret))
                },
            ),
            NativeFunction::new(
                "replace",
                ["self", "old", "new"],
                "Returns a new string with the old pattern replaced with the new",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap();
                    let owner = owner.string()?;
                    let old = args.next().unwrap();
                    let old = old.string()?;
                    let new = args.next().unwrap();
                    let new = new.string()?;
                    Ok(owner.replace(old.str(), new.str()).into())
                },
            ),
            NativeFunction::new(
                "find",
                ArgSpec::builder()
                    .req("self")
                    .req("pattern")
                    .def("start", ())
                    .def("end", ()),
                "",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    let len = owner.len();
                    let pattern = args.next().unwrap().into_string()?;
                    let start = args.next().unwrap().to_start_index(len)?;
                    let end = args.next().unwrap().to_end_index(len)?;
                    Ok(owner[start..end]
                        .find(pattern.str())
                        .map(|i| start + i)
                        .map(Value::from)
                        .unwrap_or(Value::Nil))
                },
            ),
            NativeFunction::new(
                "rfind",
                ArgSpec::builder()
                    .req("self")
                    .req("pattern")
                    .def("start", ())
                    .def("end", ()),
                "",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    let len = owner.len();
                    let pattern = args.next().unwrap().into_string()?;
                    let start = args.next().unwrap().to_start_index(len)?;
                    let end = args.next().unwrap().to_end_index(len)?;
                    Ok(owner[start..end]
                        .rfind(pattern.str())
                        .map(|i| start + i)
                        .map(Value::from)
                        .unwrap_or(Value::Nil))
                },
            ),
            NativeFunction::new("__rem", ["self", "args"], "", |globals, args, _| {
                let mut args = args.into_iter();
                let owner = args.next().unwrap().into_string()?;
                let args = args.next().unwrap().unpack(globals)?;
                Ok(Value::from(Value::format_string(owner.str(), args)?))
            }),
            NativeFunction::new(
                "__mul",
                ["self", "n"],
                "Repeats a string n times",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    let n = usize::try_from(args.next().unwrap())?;
                    let mut ret = String::new();
                    for _ in 0..n {
                        ret.push_str(&owner);
                    }
                    Ok(ret.into())
                },
            ),
            NativeFunction::new("words", ["self"], "", |_globals, args, _| {
                let mut args = args.into_iter();
                let owner = args.next().unwrap().into_string()?;
                let len = owner.len();
                let mut i = 0;
                let mut fin = false;
                Ok(NativeGenerator::new("String.words", move |_globals, _| {
                    if fin {
                        ResumeResult::Return(Value::Nil)
                    } else {
                        let j = owner[i..]
                            .find(char::is_whitespace)
                            .map(|j| i + j)
                            .unwrap_or(len);
                        let next = Value::from(&owner[i..j]);
                        i = owner[j..]
                            .find(|c: char| !c.is_whitespace())
                            .map(|i| i + j)
                            .unwrap_or(len);
                        if j == len {
                            fin = true;
                        }
                        ResumeResult::Yield(next)
                    }
                })
                .into())
            }),
            NativeFunction::new("lines", ["self"], "", |_globals, args, _| {
                let mut args = args.into_iter();
                let owner = args.next().unwrap().into_string()?;
                let len = owner.len();
                let mut i = 0;
                let mut fin = false;
                Ok(NativeGenerator::new("String.lines", move |_globals, _| {
                    if fin {
                        ResumeResult::Return(Value::Nil)
                    } else {
                        let j = owner[i..]
                            .find(|c: char| c == '\n')
                            .map(|j| i + j)
                            .unwrap_or(len);
                        let next = Value::from(&owner[i..j]);
                        i = j + 1;
                        if j == len {
                            fin = true;
                        }
                        ResumeResult::Yield(next)
                    }
                })
                .into())
            }),
            NativeFunction::new("split", ["self", "sep"], "", |_globals, args, _| {
                let mut args = args.into_iter();
                let owner = args.next().unwrap().into_string()?;
                let sep = args.next().unwrap().into_string()?;
                let len = owner.len();
                let mut i = 0;
                let mut fin = false;
                Ok(NativeGenerator::new("String.split", move |_globals, _| {
                    if fin {
                        ResumeResult::Return(Value::Nil)
                    } else {
                        let j = owner[i..].find(sep.str()).map(|j| i + j).unwrap_or(len);
                        let next = Value::from(&owner[i..j]);
                        i = j + sep.len();
                        if j == len {
                            fin = true;
                        }
                        ResumeResult::Yield(next)
                    }
                })
                .into())
            }),
            NativeFunction::new(
                "slice",
                ["self", "start", "end"],
                "",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    let len = owner.len();
                    let start = args.next().unwrap().to_start_index(len)?;
                    let end = args.next().unwrap().to_end_index(len)?;
                    Ok(Value::from(&owner[start..end]))
                },
            ),
            NativeFunction::new(
                "__slice",
                ["self", "start", "end"],
                "",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    let len = owner.len();
                    let start = args.next().unwrap().to_start_index(len)?;
                    let end = args.next().unwrap().to_end_index(len)?;
                    Ok(Value::from(&owner[start..end]))
                },
            ),
            NativeFunction::new(
                "starts_with",
                ["self", "prefix"],
                "",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    let prefix = args.next().unwrap().into_string()?;
                    Ok(owner.starts_with(prefix.str()).into())
                },
            ),
            NativeFunction::new("ends_with", ["self", "suffix"], "", |_globals, args, _| {
                let mut args = args.into_iter();
                let owner = args.next().unwrap().into_string()?;
                let suffix = args.next().unwrap().into_string()?;
                Ok(owner.ends_with(suffix.str()).into())
            }),
            NativeFunction::new(
                "trim",
                ["self"],
                "Returns self with surrounding whitespace removed",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    Ok(owner.trim().into())
                },
            ),
            NativeFunction::new(
                "rstrip",
                ["self", "suffix"],
                "Returns self with suffix removed, if it ends with the given suffix",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    let suffix = args.next().unwrap().into_string()?;
                    if owner.ends_with(suffix.str()) {
                        let stripped = &owner[..owner.len() - suffix.len()];
                        Ok(stripped.into())
                    } else {
                        Ok(owner.into())
                    }
                },
            ),
            NativeFunction::new(
                "lstrip",
                ["self", "prefix"],
                "Returns self with prefix removed, if it starts with the given prefix",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    let prefix = args.next().unwrap().into_string()?;
                    if owner.starts_with(prefix.str()) {
                        let stripped = &owner[prefix.len()..];
                        Ok(stripped.into())
                    } else {
                        Ok(owner.into())
                    }
                },
            ),
            NativeFunction::new(
                "chars",
                ArgSpec::builder().req("self").def("start", 0),
                "",
                |_globals, args, _| {
                    let mut args = args.into_iter();
                    let owner = args.next().unwrap().into_string()?;
                    let mut i = usize::try_from(args.next().unwrap())?;
                    let len = owner.len();
                    Ok(NativeGenerator::new("String.chars", move |_globals, _| {
                        while i < len && !owner.is_char_boundary(i) {
                            i += 1;
                        }
                        if i < len {
                            let ch = owner[i..].chars().next().unwrap();
                            i += 1;
                            ResumeResult::Yield(ch.into())
                        } else {
                            ResumeResult::Return(Value::Nil)
                        }
                    })
                    .into())
                },
            ),
        ]),
        HashMap::new(),
    )
}