chore: remove unused Rust projects

This commit was merged in pull request #64.
This commit is contained in:
2026-09-17 01:14:58 -04:00
parent ddc7952399
commit 247712582a
12 changed files with 0 additions and 966 deletions

View File

@@ -1,24 +0,0 @@
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
rust_library(
name = "nix_style_linter_lib",
crate_name = "nix_style_linter",
srcs = ["lib.rs"],
deps = [
"//third_party/crates:rnix",
"//third_party/crates:rowan",
],
)
rust_binary(
name = "nix-style-linter",
crate_name = "nix_style_linter_cli",
srcs = ["main.rs"],
deps = [":nix_style_linter_lib"],
)
rust_test(
name = "lib_test",
srcs = ["lib_test.rs"],
deps = [":nix_style_linter_lib"],
)

View File

@@ -1,19 +0,0 @@
[package]
name = "nix-style-linter"
version = "0.1.0"
edition = "2024"
[lib]
path = "lib.rs"
[[bin]]
name = "nix-style-linter"
path = "main.rs"
[[test]]
name = "lib_test"
path = "lib_test.rs"
[dependencies]
rnix = "0.14.0"
rowan = "0.16.1"

View File

@@ -1,522 +0,0 @@
use std::collections::HashSet;
use std::ffi::OsStr;
use std::fmt;
use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use rnix::SyntaxKind;
use rnix::ast::{self, HasEntry};
use rowan::ast::AstNode;
const PARSE_ERROR_RULE: &str = "parse-error";
const LET_ORDER_RULE: &str = "let-order";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RunStatus {
Clean,
Diagnostics,
ToolError,
}
impl RunStatus {
pub fn code(self) -> u8 {
match self {
RunStatus::Clean => 0,
RunStatus::Diagnostics => 1,
RunStatus::ToolError => 2,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Diagnostic {
pub path: PathBuf,
pub line: usize,
pub column: usize,
pub rule: &'static str,
pub message: String,
}
impl fmt::Display for Diagnostic {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"{}:{}:{}: {}: {}",
self.path.display(),
self.line,
self.column,
self.rule,
self.message
)
}
}
pub fn run(
paths: &[PathBuf],
stdout: &mut impl Write,
stderr: &mut impl Write,
) -> io::Result<RunStatus> {
if paths.is_empty() {
writeln!(stderr, "error: expected at least one path")?;
return Ok(RunStatus::ToolError);
}
let mut found_diagnostics = false;
let mut found_tool_errors = false;
for path in paths {
let path = resolve_workspace_path(path);
match collect_nix_files(&path) {
Ok(files) => {
for file in files {
match lint_file(&file) {
Ok(diagnostics) => {
for diagnostic in diagnostics {
found_diagnostics = true;
writeln!(stdout, "{diagnostic}")?;
}
}
Err(error) => {
found_tool_errors = true;
writeln!(
stderr,
"error: failed to read {path}: {error}",
path = file.display()
)?;
}
}
}
}
Err(error) => {
found_tool_errors = true;
writeln!(stderr, "error: {error}")?;
}
}
}
if found_tool_errors {
Ok(RunStatus::ToolError)
} else if found_diagnostics {
Ok(RunStatus::Diagnostics)
} else {
Ok(RunStatus::Clean)
}
}
fn resolve_workspace_path(path: &Path) -> PathBuf {
if path.is_absolute() {
return path.to_path_buf();
}
if let Some(workspace) = std::env::var_os("BUILD_WORKSPACE_DIRECTORY") {
return PathBuf::from(workspace).join(path);
}
path.to_path_buf()
}
pub fn lint_file(path: &Path) -> io::Result<Vec<Diagnostic>> {
let source = fs::read_to_string(path)?;
let parse = rnix::Root::parse(&source);
let parse_diagnostics = parse_diagnostics(path, &source, parse.errors());
if !parse_diagnostics.is_empty() {
return Ok(parse_diagnostics);
}
Ok(let_order_diagnostics(path, &source, &parse.tree()))
}
fn parse_diagnostics(
path: &Path,
source: &str,
errors: &[rnix::ParseError],
) -> Vec<Diagnostic> {
errors
.iter()
.map(|error| {
let (line, column) = line_column(source, parse_error_offset(error, source.len()));
Diagnostic {
path: path.to_path_buf(),
line,
column,
rule: PARSE_ERROR_RULE,
message: error.to_string(),
}
})
.collect()
}
fn let_order_diagnostics(path: &Path, source: &str, root: &ast::Root) -> Vec<Diagnostic> {
root.syntax()
.descendants()
.filter_map(ast::LetIn::cast)
.flat_map(|let_in| let_order_diagnostics_for_let(path, source, &let_in))
.collect()
}
fn let_order_diagnostics_for_let(
path: &Path,
source: &str,
let_in: &ast::LetIn,
) -> Vec<Diagnostic> {
let bindings: Vec<_> = let_in
.attrpath_values()
.filter_map(|binding| plain_binding(source, &binding))
.collect();
let names: Vec<_> = bindings
.iter()
.map(|binding| binding.name.clone())
.collect();
let mut diagnostics = Vec::new();
for (index, binding) in bindings.iter().enumerate() {
let later_names: HashSet<_> = names.iter().skip(index + 1).cloned().collect();
if let Some(referenced_name) =
first_forward_reference(binding.value.syntax(), &later_names, &mut HashSet::new())
{
if is_suppressed(source, binding.line, LET_ORDER_RULE) {
continue;
}
diagnostics.push(Diagnostic {
path: path.to_path_buf(),
line: binding.line,
column: binding.column,
rule: LET_ORDER_RULE,
message: format!(
"`{binding}` references later let binding `{referenced}`",
binding = binding.name,
referenced = referenced_name
),
});
}
}
diagnostics
}
#[derive(Debug, Clone)]
struct Binding {
name: String,
line: usize,
column: usize,
value: ast::Expr,
}
fn plain_binding(source: &str, binding: &ast::AttrpathValue) -> Option<Binding> {
let attr = plain_attr(binding.attrpath()?)?;
let value = binding.value()?;
let offset = usize::from(attr.text_range().start());
let (line, column) = line_column(source, offset);
Some(Binding {
name: attr.text().to_string(),
line,
column,
value,
})
}
fn plain_attrpath_name(binding: &ast::AttrpathValue) -> Option<String> {
let attr = plain_attr(binding.attrpath()?)?;
Some(attr.text().to_string())
}
fn plain_attr(attrpath: ast::Attrpath) -> Option<rnix::SyntaxToken> {
let mut attrs = attrpath.attrs();
let attr = attrs.next()?;
if attrs.next().is_some() {
return None;
}
match attr {
ast::Attr::Ident(ident) => ident.ident_token(),
ast::Attr::Dynamic(_) | ast::Attr::Str(_) => None,
}
}
fn first_forward_reference(
node: &rnix::SyntaxNode,
later_names: &HashSet<String>,
shadowed: &mut HashSet<String>,
) -> Option<String> {
match node.kind() {
SyntaxKind::NODE_LAMBDA => {
return with_shadowed(shadowed, lambda_bound_names(node), |shadowed| {
first_forward_reference_in_children(node, later_names, shadowed)
});
}
SyntaxKind::NODE_LET_IN => {
return with_shadowed(shadowed, plain_let_binding_names(node), |shadowed| {
first_forward_reference_in_children(node, later_names, shadowed)
});
}
SyntaxKind::NODE_ATTR_SET if rec_attrset_binds_names(node) => {
return with_shadowed(shadowed, plain_attrset_binding_names(node), |shadowed| {
first_forward_reference_in_children(node, later_names, shadowed)
});
}
_ => {}
}
if is_reference_ident(node) {
if let Some(name) = ident_name(node) {
if later_names.contains(&name) && !shadowed.contains(&name) {
return Some(name);
}
}
}
first_forward_reference_in_children(node, later_names, shadowed)
}
fn first_forward_reference_in_children(
node: &rnix::SyntaxNode,
later_names: &HashSet<String>,
shadowed: &mut HashSet<String>,
) -> Option<String> {
node.children()
.find_map(|child| first_forward_reference(&child, later_names, shadowed))
}
fn with_shadowed<R>(
shadowed: &mut HashSet<String>,
names: Vec<String>,
action: impl FnOnce(&mut HashSet<String>) -> R,
) -> R {
let inserted: Vec<_> = names
.into_iter()
.filter(|name| shadowed.insert(name.clone()))
.collect();
let result = action(shadowed);
for name in inserted {
shadowed.remove(&name);
}
result
}
fn is_reference_ident(node: &rnix::SyntaxNode) -> bool {
if node.kind() != SyntaxKind::NODE_IDENT {
return false;
}
!matches!(
node.parent().map(|parent| parent.kind()),
Some(
SyntaxKind::NODE_ATTRPATH
| SyntaxKind::NODE_IDENT_PARAM
| SyntaxKind::NODE_PAT_BIND
| SyntaxKind::NODE_PAT_ENTRY
)
)
}
fn ident_name(node: &rnix::SyntaxNode) -> Option<String> {
ast::Ident::cast(node.clone())
.and_then(|ident| ident.ident_token())
.map(|token| token.text().to_string())
}
fn lambda_bound_names(node: &rnix::SyntaxNode) -> Vec<String> {
let Some(lambda) = ast::Lambda::cast(node.clone()) else {
return Vec::new();
};
let Some(param) = lambda.param() else {
return Vec::new();
};
match param {
ast::Param::IdentParam(param) => param
.ident()
.and_then(|ident| ident.ident_token())
.map(|token| vec![token.text().to_string()])
.unwrap_or_default(),
ast::Param::Pattern(pattern) => {
let mut names: Vec<_> = pattern
.pat_entries()
.filter_map(|entry| entry.ident())
.filter_map(|ident| ident.ident_token())
.map(|token| token.text().to_string())
.collect();
if let Some(name) = pattern
.pat_bind()
.and_then(|bind| bind.ident())
.and_then(|ident| ident.ident_token())
.map(|token| token.text().to_string())
{
names.push(name);
}
names
}
}
}
fn plain_let_binding_names(node: &rnix::SyntaxNode) -> Vec<String> {
ast::LetIn::cast(node.clone())
.map(|let_in| {
let_in
.attrpath_values()
.filter_map(|binding| plain_attrpath_name(&binding))
.collect()
})
.unwrap_or_default()
}
fn rec_attrset_binds_names(node: &rnix::SyntaxNode) -> bool {
ast::AttrSet::cast(node.clone())
.and_then(|attrset| attrset.rec_token())
.is_some()
}
fn plain_attrset_binding_names(node: &rnix::SyntaxNode) -> Vec<String> {
ast::AttrSet::cast(node.clone())
.map(|attrset| {
attrset
.attrpath_values()
.filter_map(|binding| plain_attrpath_name(&binding))
.collect()
})
.unwrap_or_default()
}
fn is_suppressed(source: &str, line: usize, rule: &str) -> bool {
let Some(previous_line_index) = line.checked_sub(2) else {
return false;
};
let Some(previous_line) = source.lines().nth(previous_line_index) else {
return false;
};
let Some(comment) = previous_line.trim_start().strip_prefix('#') else {
return false;
};
let Some(rest) = comment.trim().strip_prefix("nix-style-linter:") else {
return false;
};
let Some((directive, reason)) = rest.trim().split_once("--") else {
return false;
};
let mut words = directive.split_whitespace();
words.next() == Some("disable-next-line")
&& words.any(|word| word == rule)
&& !reason.trim().is_empty()
}
fn collect_nix_files(path: &Path) -> Result<Vec<PathBuf>, InputError> {
let metadata = fs::metadata(path).map_err(|source| InputError::Metadata {
path: path.to_path_buf(),
source,
})?;
if metadata.is_file() {
return Ok(is_nix_file(path)
.then(|| path.to_path_buf())
.into_iter()
.collect());
}
if metadata.is_dir() {
let mut files = Vec::new();
collect_nix_files_from_dir(path, &mut files)?;
files.sort();
return Ok(files);
}
Err(InputError::UnsupportedPath(path.to_path_buf()))
}
fn collect_nix_files_from_dir(path: &Path, files: &mut Vec<PathBuf>) -> Result<(), InputError> {
let entries = fs::read_dir(path).map_err(|source| InputError::ReadDir {
path: path.to_path_buf(),
source,
})?;
for entry in entries {
let entry = entry.map_err(|source| InputError::ReadDir {
path: path.to_path_buf(),
source,
})?;
let entry_path = entry.path();
let file_type = entry.file_type().map_err(|source| InputError::Metadata {
path: entry_path.clone(),
source,
})?;
if file_type.is_dir() {
collect_nix_files_from_dir(&entry_path, files)?;
} else if file_type.is_file() && is_nix_file(&entry_path) {
files.push(entry_path);
}
}
Ok(())
}
fn is_nix_file(path: &Path) -> bool {
path.extension() == Some(OsStr::new("nix"))
}
fn parse_error_offset(error: &rnix::ParseError, end_offset: usize) -> usize {
match error {
rnix::ParseError::Unexpected(range)
| rnix::ParseError::UnexpectedExtra(range)
| rnix::ParseError::UnexpectedWanted(_, range, _)
| rnix::ParseError::UnexpectedDoubleBind(range)
| rnix::ParseError::DuplicatedArgs(range, _) => usize::from(range.start()),
rnix::ParseError::UnexpectedEOF
| rnix::ParseError::UnexpectedEOFWanted(_)
| rnix::ParseError::RecursionLimitExceeded => end_offset,
_ => end_offset,
}
}
fn line_column(source: &str, offset: usize) -> (usize, usize) {
let target = offset.min(source.len());
let mut line = 1;
let mut column = 1;
for (index, character) in source.char_indices() {
if index >= target {
break;
}
if character == '\n' {
line += 1;
column = 1;
} else {
column += 1;
}
}
(line, column)
}
#[derive(Debug)]
enum InputError {
Metadata { path: PathBuf, source: io::Error },
ReadDir { path: PathBuf, source: io::Error },
UnsupportedPath(PathBuf),
}
impl fmt::Display for InputError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InputError::Metadata { path, source } => {
write!(formatter, "failed to inspect {}: {source}", path.display())
}
InputError::ReadDir { path, source } => {
write!(formatter, "failed to read directory {}: {source}", path.display())
}
InputError::UnsupportedPath(path) => {
write!(formatter, "unsupported path type: {}", path.display())
}
}
}
}

View File

@@ -1,281 +0,0 @@
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use nix_style_linter::RunStatus;
static TEST_DIRECTORY_ID: AtomicUsize = AtomicUsize::new(0);
#[test]
fn malformed_file_reports_parse_error() {
let workspace = TestWorkspace::new("malformed_file_reports_parse_error");
let path = workspace.write("broken.nix", "let x = ; in x\n");
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Diagnostics);
assert_eq!(output.stderr, "");
assert!(output.stdout.starts_with(&format!(
"{}:1:9: parse-error: unexpected TOKEN_SEMICOLON at 8..9",
workspace.path("broken.nix").display()
)));
assert!(!output.stdout.contains("let-order"));
}
#[test]
fn forward_reference_reports_let_order() {
let workspace = TestWorkspace::new("forward_reference_reports_let_order");
let path = workspace.write(
"forward.nix",
"let\n app = package;\n package = \"hello\";\nin app\n",
);
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Diagnostics);
assert_eq!(output.stderr, "");
assert_eq!(
output.stdout,
format!(
"{}:2:3: let-order: `app` references later let binding `package`\n",
workspace.path("forward.nix").display()
)
);
}
#[test]
fn dependency_ordered_bindings_exit_clean() {
let workspace = TestWorkspace::new("dependency_ordered_bindings_exit_clean");
let path = workspace.write(
"ordered.nix",
"let\n package = \"hello\";\n app = package;\nin app\n",
);
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Clean);
assert_eq!(output.stdout, "");
assert_eq!(output.stderr, "");
}
#[test]
fn outer_scope_reference_exits_clean() {
let workspace = TestWorkspace::new("outer_scope_reference_exits_clean");
let path = workspace.write(
"outer.nix",
"let\n package = \"hello\";\nin\nlet\n app = package;\nin app\n",
);
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Clean);
assert_eq!(output.stdout, "");
assert_eq!(output.stderr, "");
}
#[test]
fn nested_lambda_parameter_shadows_later_binding() {
let workspace = TestWorkspace::new("nested_lambda_parameter_shadows_later_binding");
let path = workspace.write(
"lambda.nix",
"let\n call = package: package;\n package = \"hello\";\nin call\n",
);
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Clean);
assert_eq!(output.stdout, "");
assert_eq!(output.stderr, "");
}
#[test]
fn nested_let_binding_shadows_later_binding() {
let workspace = TestWorkspace::new("nested_let_binding_shadows_later_binding");
let path = workspace.write(
"nested-let.nix",
"let\n app = let package = \"inner\"; in package;\n package = \"outer\";\nin app\n",
);
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Clean);
assert_eq!(output.stdout, "");
assert_eq!(output.stderr, "");
}
#[test]
fn earlier_sibling_constant_shared_by_multiple_bindings_exits_clean() {
let workspace = TestWorkspace::new("earlier_sibling_constant_shared_by_multiple_bindings");
let path = workspace.write(
"shared.nix",
"let\n package = \"hello\";\n app = package;\n shell = package;\nin [ app shell ]\n",
);
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Clean);
assert_eq!(output.stdout, "");
assert_eq!(output.stderr, "");
}
#[test]
fn previous_line_suppression_disables_let_order() {
let workspace = TestWorkspace::new("previous_line_suppression_disables_let_order");
let path = workspace.write(
"suppressed.nix",
"let\n # nix-style-linter: disable-next-line let-order -- intentional recursion\n app = package;\n package = app;\nin app\n",
);
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Clean);
assert_eq!(output.stdout, "");
assert_eq!(output.stderr, "");
}
#[test]
fn malformed_suppression_does_not_disable_let_order() {
let workspace = TestWorkspace::new("malformed_suppression_does_not_disable_let_order");
let path = workspace.write(
"unsuppressed.nix",
"let\n # nix-style-linter: disable-next-line let-order\n app = package;\n package = \"hello\";\nin app\n",
);
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Diagnostics);
assert_eq!(
output.stdout,
format!(
"{}:3:3: let-order: `app` references later let binding `package`\n",
workspace.path("unsuppressed.nix").display()
)
);
assert_eq!(output.stderr, "");
}
#[test]
fn unsupported_binding_syntax_is_skipped() {
let workspace = TestWorkspace::new("unsupported_binding_syntax_is_skipped");
let path = workspace.write(
"unsupported.nix",
"let\n attrs.value = package;\n inherit package;\n package = \"hello\";\nin attrs\n",
);
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Clean);
assert_eq!(output.stdout, "");
assert_eq!(output.stderr, "");
}
#[test]
fn valid_file_exits_clean() {
let workspace = TestWorkspace::new("valid_file_exits_clean");
let path = workspace.write("valid.nix", "let x = 1; in x\n");
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Clean);
assert_eq!(output.stdout, "");
assert_eq!(output.stderr, "");
}
#[test]
fn missing_path_reports_tool_error() {
let workspace = TestWorkspace::new("missing_path_reports_tool_error");
let path = workspace.path("missing.nix");
let output = run_linter(&[path.clone()]);
assert_eq!(output.status, RunStatus::ToolError);
assert_eq!(output.stdout, "");
assert!(output.stderr.starts_with(&format!(
"error: failed to inspect {}:",
path.display()
)));
}
#[test]
fn directory_inputs_recurse_through_nix_files() {
let workspace = TestWorkspace::new("directory_inputs_recurse_through_nix_files");
workspace.write("ignored.txt", "let x = ; in x\n");
workspace.write("nested/broken.nix", "let\n x = 1;\n y = ;\nin y\n");
let output = run_linter(&[workspace.root.clone()]);
assert_eq!(output.status, RunStatus::Diagnostics);
assert_eq!(output.stderr, "");
assert!(output.stdout.starts_with(&format!(
"{}:3:7: parse-error: unexpected TOKEN_SEMICOLON at 19..20",
workspace.path("nested/broken.nix").display()
)));
assert!(!output.stdout.contains("ignored.txt"));
}
#[test]
fn empty_invocation_reports_tool_error() {
let output = run_linter(&[]);
assert_eq!(output.status, RunStatus::ToolError);
assert_eq!(output.stdout, "");
assert_eq!(output.stderr, "error: expected at least one path\n");
}
struct LinterOutput {
status: RunStatus,
stdout: String,
stderr: String,
}
fn run_linter(paths: &[PathBuf]) -> LinterOutput {
let mut stdout = Vec::new();
let mut stderr = Vec::new();
let status = nix_style_linter::run(paths, &mut stdout, &mut stderr).unwrap();
LinterOutput {
status,
stdout: String::from_utf8(stdout).unwrap(),
stderr: String::from_utf8(stderr).unwrap(),
}
}
struct TestWorkspace {
root: PathBuf,
}
impl TestWorkspace {
fn new(name: &str) -> Self {
let id = TEST_DIRECTORY_ID.fetch_add(1, Ordering::Relaxed);
let root = std::env::temp_dir().join(format!(
"nix-style-linter-{process}-{name}-{id}",
process = std::process::id()
));
if root.exists() {
fs::remove_dir_all(&root).unwrap();
}
fs::create_dir_all(&root).unwrap();
Self { root }
}
fn path(&self, relative: impl AsRef<Path>) -> PathBuf {
self.root.join(relative)
}
fn write(&self, relative: impl AsRef<Path>, contents: &str) -> PathBuf {
let path = self.path(relative);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).unwrap();
}
fs::write(&path, contents).unwrap();
path
}
}
impl Drop for TestWorkspace {
fn drop(&mut self) {
fs::remove_dir_all(&self.root).unwrap();
}
}

View File

@@ -1,16 +0,0 @@
use std::path::PathBuf;
use std::process::ExitCode;
fn main() -> ExitCode {
let paths: Vec<PathBuf> = std::env::args_os().skip(1).map(PathBuf::from).collect();
let mut stdout = std::io::stdout().lock();
let mut stderr = std::io::stderr().lock();
match nix_style_linter::run(&paths, &mut stdout, &mut stderr) {
Ok(status) => ExitCode::from(status.code()),
Err(error) => {
eprintln!("error: internal error: {error}");
ExitCode::from(2)
}
}
}

View File

@@ -1,20 +0,0 @@
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
rust_library(
name = "xatu_lib",
crate_name = "xatu",
srcs = ["lib.rs"],
)
rust_binary(
name = "xatu",
crate_name = "xatu_cli",
srcs = ["main.rs"],
deps = [":xatu_lib"],
)
rust_test(
name = "lib_test",
srcs = ["lib_test.rs"],
deps = [":xatu_lib"],
)

View File

@@ -1,13 +0,0 @@
[package]
name = "xatu"
version = "0.1.0"
edition = "2024"
[lib]
path = "lib.rs"
[[bin]]
name = "xatu"
path = "main.rs"
[dependencies]

View File

@@ -1,5 +0,0 @@
pub const GREETING: &str = "Hello from Xatu";
pub fn greeting() -> &'static str {
GREETING
}

View File

@@ -1,4 +0,0 @@
#[test]
fn greeting_returns_hello_message() {
assert_eq!(xatu::greeting(), "Hello from Xatu");
}

View File

@@ -1,3 +0,0 @@
fn main() {
println!("{}", xatu::greeting());
}