Skip to main content

forehead_core/
template.rs

1// بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيم
2// This file is part of forehead.
3//
4// Copyright (C) 2026-Present Afsall Inc.
5// SPDX-License-Identifier: Apache-2.0 OR MIT
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11// http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18//
19// Alternatively, this file is available under the MIT License:
20// Permission is hereby granted, free of charge, to any person obtaining a copy
21// of this software and associated documentation files (the "Software"), to deal
22// in the Software without restriction, including without limitation the rights
23// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24// copies of the Software, and to permit persons to whom the Software is
25// furnished to do so, subject to the following conditions:
26//
27// The above copyright notice and this permission notice shall be included in all
28// copies or substantial portions of the Software.
29//
30// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36// SOFTWARE.
37
38use crate::{config::Substitution, error::ForeheadError};
39use std::{fs, path::Path};
40
41#[derive(Debug, Clone)]
42pub struct HeaderTemplate {
43    pub content: String,
44    pub lines: Vec<String>,
45}
46
47impl HeaderTemplate {
48    pub fn from_file(path: &Path) -> Result<Self, ForeheadError> {
49        let content = fs::read_to_string(path).map_err(|e| {
50            ForeheadError::Template(format!("failed to read {}: {e}", path.display()))
51        })?;
52        let lines: Vec<String> = content.lines().map(|l| l.to_string()).collect();
53        Ok(HeaderTemplate { content, lines })
54    }
55
56    pub fn new(content: &str) -> Self {
57        let lines: Vec<String> = content.lines().map(|l| l.to_string()).collect();
58        HeaderTemplate {
59            content: content.to_string(),
60            lines,
61        }
62    }
63
64    pub fn substitute(&self, subst: &Substitution) -> String {
65        let mut result = String::new();
66        for line in &self.lines {
67            let rendered = line
68                .replace("{project}", &subst.project)
69                .replace("{author}", &subst.author)
70                .replace("{year}", &subst.year)
71                .replace("{year_span}", &subst.year_span)
72                .replace("{license}", &subst.license)
73                .replace("{repository}", &subst.repository)
74                .replace("{description}", &subst.description)
75                .replace("{file}", &subst.file);
76            result.push_str(&rendered);
77            result.push('\n');
78        }
79        result
80    }
81
82    /// Returns the template as a list of comment lines with the given comment style.
83    /// Each line has the comment prefix applied.
84    pub fn as_comment_lines(&self, comment_prefix: &str, subst: &Substitution) -> Vec<String> {
85        let mut result = Vec::new();
86        for line in &self.lines {
87            let rendered = line
88                .replace("{project}", &subst.project)
89                .replace("{author}", &subst.author)
90                .replace("{year}", &subst.year)
91                .replace("{year_span}", &subst.year_span)
92                .replace("{license}", &subst.license)
93                .replace("{repository}", &subst.repository)
94                .replace("{description}", &subst.description)
95                .replace("{file}", &subst.file);
96            if rendered.trim().is_empty() {
97                result.push(comment_prefix.to_string());
98            } else {
99                result.push(format!("{} {}", comment_prefix, rendered.trim()));
100            }
101        }
102        result
103    }
104}