1use crate::Library;
2use crate::diag::FileResult;
3use compose_codespan_reporting::files::Files;
4use compose_syntax::{FileId, Source, Span};
5use std::io::{Read, Write};
6use std::ops::Range;
7
8pub trait World {
24 fn entry_point(&self) -> FileId;
26
27 fn source(&self, file_id: FileId) -> FileResult<Source>;
33
34 fn library(&self) -> &Library;
36
37 fn write(
42 &self,
43 f: &mut dyn FnMut(&mut dyn Write) -> std::io::Result<()>,
44 ) -> std::io::Result<()>;
45
46 fn read(&self, f: &mut dyn FnMut(&mut dyn Read) -> std::io::Result<()>) -> std::io::Result<()>;
51
52 fn with_io(
57 &self,
58 f: &mut dyn FnMut(&mut dyn Read, &mut dyn Write) -> std::io::Result<()>,
59 ) -> std::io::Result<()> {
60 self.read(&mut |r| self.write(&mut |w| f(r, w)))
61 }
62
63 fn name(&self, id: FileId) -> String {
67 id.path().0.display().to_string()
68 }
69
70 fn related_source(&self, span: Span) -> Option<Source> {
74 self.source(span.id()?).ok()
75 }
76}
77
78pub struct SyntaxContext<'a> {
79 pub world: &'a dyn World,
80}
81
82impl<'a> Files<'a> for &dyn World {
83 type FileId = FileId;
84 type Name = String;
85 type Source = Source;
86
87 fn name(
88 &'a self,
89 id: Self::FileId,
90 ) -> Result<Self::Name, compose_codespan_reporting::files::Error> {
91 Ok(World::name(*self, id))
92 }
93
94 fn source(
95 &'a self,
96 id: Self::FileId,
97 ) -> Result<Self::Source, compose_codespan_reporting::files::Error> {
98 World::source(*self, id).map_err(|_| compose_codespan_reporting::files::Error::FileMissing)
99 }
100
101 fn line_index(
102 &'a self,
103 id: Self::FileId,
104 byte_index: usize,
105 ) -> Result<usize, compose_codespan_reporting::files::Error> {
106 let source = Files::source(self, id)?;
107 Ok(source
108 .line_starts()
109 .binary_search(&byte_index)
110 .unwrap_or_else(|next_line| next_line - 1))
111 }
112
113 fn line_range(
114 &'a self,
115 id: Self::FileId,
116 line_index: usize,
117 ) -> Result<Range<usize>, compose_codespan_reporting::files::Error> {
118 let source = Files::source(self, id)?;
119 let start = source.line_starts().get(line_index).copied().ok_or(
120 compose_codespan_reporting::files::Error::LineTooLarge {
121 given: line_index,
122 max: source.line_starts().len(),
123 },
124 )?;
125
126 let end = source
127 .line_starts()
128 .get(line_index + 1)
129 .copied()
130 .unwrap_or(source.text().len());
131
132 Ok(start..end)
133 }
134}