Skip to main content

microcad_lang/lower/ir/
source.rs

1// Copyright © 2024-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! µcad source file representation
5
6use crate::lower::ir;
7
8use microcad_lang_base::{
9    ComputedHash, Hashed, Identifier, LineCol, ResourceLocation, SourceLocInfo, SrcRef,
10    SrcReferrer, Url,
11};
12
13/// µcad source file
14#[derive(Clone, Debug)]
15pub struct Source {
16    /// Documentation.
17    pub doc: Option<ir::DocBlock>,
18    /// Qualified name of the file if loaded from externals
19    pub name: ir::QualifiedName,
20    /// Root code body.
21    pub statements: ir::StatementList,
22    /// Name of loaded file.
23    pub url: Url,
24    /// Source file string with hash
25    pub source: Hashed<String>,
26    /// Line offset
27    pub line_offset: u32,
28}
29
30impl Source {
31    /// Create new source file from existing source.
32    pub fn new(
33        doc: Option<ir::DocBlock>,
34        statements: ir::StatementList,
35        source: Hashed<String>,
36        url: Url,
37    ) -> Self {
38        Self {
39            doc,
40            statements,
41            source,
42            url,
43            name: ir::QualifiedName::default(),
44            line_offset: 0,
45        }
46    }
47
48    pub fn with_line_offset(self, line_offset: u32) -> Self {
49        let mut src = self;
50        src.line_offset = line_offset;
51        src
52    }
53
54    /// Return the module name from the file name
55    pub fn id(&self) -> Identifier {
56        self.name.last().unwrap_or(&Identifier::none()).clone()
57    }
58
59    /// Return filename of loaded file or `<NO FILE>`
60    pub fn filename(&self) -> std::path::PathBuf {
61        self.to_file_path()
62            .unwrap_or(std::path::PathBuf::from("<NO FILE>"))
63    }
64
65    /// Return filename of loaded file or `<NO FILE>`
66    pub fn set_filename(&mut self, path: impl AsRef<std::path::Path>) {
67        assert!(self.to_file_path().is_none());
68        self.url = Url::from_file_path(
69            path.as_ref()
70                .canonicalize()
71                .unwrap_or(path.as_ref().to_path_buf()),
72        )
73        .unwrap_or(self.url.clone());
74    }
75
76    /// get a specific line
77    ///
78    /// - `line`: line number beginning at `0`
79    pub fn get_code(&self, src_ref: &SrcRef) -> &str {
80        let range = &src_ref.range;
81        &self.source[range.start..range.end]
82    }
83
84    /// Set file name.
85    pub fn set_name(&mut self, name: ir::QualifiedName) {
86        self.name = name
87    }
88
89    /// Get a miette source adapter for the SourceFile
90    pub fn source_loc_info<'a>(&'a self) -> SourceLocInfo<'a> {
91        SourceLocInfo {
92            code: &self.source,
93            url: self.url.clone(),
94            line_offset: self.line_offset,
95        }
96    }
97}
98
99impl ResourceLocation for Source {
100    fn url(&self) -> &microcad_lang_base::Url {
101        &self.url
102    }
103}
104
105impl std::fmt::Display for Source {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        self.statements.iter().try_for_each(|s| writeln!(f, "{s}"))
108    }
109}
110
111impl SrcReferrer for Source {
112    fn src_ref(&self) -> SrcRef {
113        SrcRef::new(
114            0..self.source.len(),
115            LineCol::default(),
116            self.source.computed_hash(),
117        )
118    }
119}