use std::ffi::CString;
use vala_sys as ffi;
use crate::context::SourceFileType;
use crate::object::{opt_string, take_string, RawWrapper};
use crate::{CodeContext, SourceFile};
impl SourceFile {
pub fn new(
context: &CodeContext,
file_type: SourceFileType,
filename: &str,
content: Option<&str>,
) -> Self {
let cfilename = CString::new(filename).expect("filename contains NUL");
let ccontent = content.map(|c| CString::new(c).expect("content contains NUL"));
let content_ptr = ccontent.as_ref().map_or(std::ptr::null(), |c| c.as_ptr());
unsafe {
Self::from_raw_full(ffi::vala_source_file_new(
context.as_raw(),
CodeContext::source_file_type(file_type),
cfilename.as_ptr(),
content_ptr,
glib_sys::GFALSE,
))
.expect("vala_source_file_new returned null")
}
}
pub fn filename(&self) -> Option<String> {
unsafe { opt_string(ffi::vala_source_file_get_filename(self.as_raw())) }
}
pub fn content(&self) -> Option<String> {
unsafe { opt_string(ffi::vala_source_file_get_content(self.as_raw())) }
}
pub fn source_line(&self, lineno: i32) -> Option<String> {
unsafe { take_string(ffi::vala_source_file_get_source_line(self.as_raw(), lineno)) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SourceLocation {
pub line: i32,
pub column: i32,
}
impl SourceLocation {
pub(crate) unsafe fn from_ffi(raw: ffi::ValaSourceLocation) -> Self {
SourceLocation {
line: raw.line,
column: raw.column,
}
}
}
impl crate::SourceReference {
pub fn file(&self) -> SourceFile {
unsafe {
SourceFile::from_raw_none(ffi::vala_source_reference_get_file(self.as_raw()))
.expect("source reference had no file")
}
}
pub fn begin(&self) -> SourceLocation {
unsafe {
let mut loc = std::mem::zeroed::<ffi::ValaSourceLocation>();
ffi::vala_source_reference_get_begin(self.as_raw(), &mut loc);
SourceLocation::from_ffi(loc)
}
}
pub fn end(&self) -> SourceLocation {
unsafe {
let mut loc = std::mem::zeroed::<ffi::ValaSourceLocation>();
ffi::vala_source_reference_get_end(self.as_raw(), &mut loc);
SourceLocation::from_ffi(loc)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::CodeContext;
fn with_file<T>(src: &str, f: impl FnOnce(&SourceFile) -> T) -> T {
let ctx = CodeContext::new();
ctx.with_current(|ctx| {
let file = SourceFile::new(ctx, SourceFileType::Source, "t.vala", Some(src));
ctx.add_source_file(&file);
f(&file)
})
}
#[test]
fn content_round_trips() {
let src = "class Foo {\n}\n";
assert_eq!(with_file(src, |f| f.content()), Some(src.to_string()));
}
#[test]
fn source_line_is_one_based_and_drops_the_newline() {
let src = "class Foo {\n int x;\n}\n";
let lines = with_file(src, |f| {
(f.source_line(1), f.source_line(2), f.source_line(3))
});
assert_eq!(lines.0, Some("class Foo {".to_string()));
assert_eq!(lines.1, Some(" int x;".to_string()));
assert_eq!(lines.2, Some("}".to_string()));
}
#[test]
fn source_line_out_of_range_is_none() {
let src = "class Foo {\n}\n";
assert_eq!(with_file(src, |f| f.source_line(99)), None);
}
}