Skip to content

Commit

Permalink
feat(xml): support xmllint (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
hougesen authored Mar 18, 2024
1 parent cacfbee commit 2b335e3
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,6 @@ mdsf init
| TOML | `taplo` |
| TypeScript | `prettier`, `biome`, `deno_fmt` |
| Vue | `prettier` |
| Xml | `xmllint` |
| YAML | `prettier` |
| Zig | `zigfmt` |
40 changes: 40 additions & 0 deletions schemas/v0.0.1/mdsf.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,17 @@
}
]
},
"xml": {
"default": {
"enabled": true,
"formatter": "xmllint"
},
"allOf": [
{
"$ref": "#/definitions/Lang_for_Xml"
}
]
},
"yaml": {
"default": {
"enabled": true,
Expand Down Expand Up @@ -857,6 +868,18 @@
}
}
},
"Lang_for_Xml": {
"type": "object",
"required": ["enabled", "formatter"],
"properties": {
"enabled": {
"type": "boolean"
},
"formatter": {
"$ref": "#/definitions/MdsfFormatter_for_Xml"
}
}
},
"Lang_for_Yaml": {
"type": "object",
"required": ["enabled", "formatter"],
Expand Down Expand Up @@ -1318,6 +1341,19 @@
}
]
},
"MdsfFormatter_for_Xml": {
"anyOf": [
{
"$ref": "#/definitions/Xml"
},
{
"type": "array",
"items": {
"$ref": "#/definitions/MdsfFormatter_for_Xml"
}
}
]
},
"MdsfFormatter_for_Yaml": {
"anyOf": [
{
Expand Down Expand Up @@ -1400,6 +1436,10 @@
"type": "string",
"enum": ["prettier"]
},
"Xml": {
"type": "string",
"enum": ["xmllint"]
},
"Yaml": {
"type": "string",
"enum": ["prettier"]
Expand Down
6 changes: 5 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::languages::{
javascript::JavaScript, json::Json, just::Just, lua::Lua, markdown::Markdown, nim::Nim,
objective_c::ObjectiveC, ocaml::OCaml, protobuf::Protobuf, python::Python, rescript::ReScript,
roc::Roc, ruby::Ruby, rust::Rust, shell::Shell, sql::Sql, toml::Toml, typescript::TypeScript,
vue::Vue, yaml::Yaml, zig::Zig, Lang,
vue::Vue, xml::Xml, yaml::Yaml, zig::Zig, Lang,
};

#[derive(Debug, serde::Serialize, serde::Deserialize, JsonSchema)]
Expand Down Expand Up @@ -115,6 +115,9 @@ pub struct MdsfConfig {
#[serde(default)]
pub vue: Lang<Vue>,

#[serde(default)]
pub xml: Lang<Xml>,

#[serde(default)]
pub yaml: Lang<Yaml>,

Expand Down Expand Up @@ -161,6 +164,7 @@ impl Default for MdsfConfig {
toml: Lang::<Toml>::default(),
typescript: Lang::<TypeScript>::default(),
vue: Lang::<Vue>::default(),
xml: Lang::<Xml>::default(),
yaml: Lang::<Yaml>::default(),
zig: Lang::<Zig>::default(),
}
Expand Down
2 changes: 2 additions & 0 deletions src/formatters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub mod sqlfluff;
pub mod stylua;
pub mod taplo;
pub mod usort;
pub mod xmllint;
pub mod yapf;
pub mod zigfmt;

Expand Down Expand Up @@ -134,6 +135,7 @@ pub fn format_snippet(config: &MdsfConfig, language: &Language, code: &str) -> S
Language::Yaml => config.yaml.format(snippet_path),
Language::Zig => config.zig.format(snippet_path),
Language::ReScript => config.rescript.format(snippet_path),
Language::Xml => config.xml.format(snippet_path),
} {
let mut f = formatted_code.trim().to_owned();

Expand Down
53 changes: 53 additions & 0 deletions src/formatters/xmllint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use super::execute_command;

#[inline]
pub fn format_using_xmllint(
snippet_path: &std::path::Path,
) -> std::io::Result<(bool, Option<String>)> {
let mut cmd = std::process::Command::new("xmllint");

cmd.arg("--format")
.arg(snippet_path)
.arg("--output")
.arg(snippet_path);

execute_command(&mut cmd, snippet_path)
}

#[cfg(test)]
mod test_isort {
use crate::{formatters::setup_snippet, languages::Language};

use super::format_using_xmllint;

#[test_with::executable(xmllint)]
#[test]
fn it_should_format_xml() {
let input = "
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>";

let expected_output = r#"<?xml version="1.0"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
"#;

let snippet = setup_snippet(input, Language::Python.to_file_ext())
.expect("it to create a snippet file");

let output = format_using_xmllint(snippet.path())
.expect("it to be successful")
.1
.expect("it to be some");

assert_eq!(expected_output, output);
}
}
4 changes: 4 additions & 0 deletions src/languages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub enum Language {
Toml,
TypeScript,
Vue,
Xml,
Yaml,
Zig,
// TODO: Haskell,
Expand Down Expand Up @@ -86,6 +87,7 @@ pub mod sql;
pub mod toml;
pub mod typescript;
pub mod vue;
pub mod xml;
pub mod yaml;
pub mod zig;

Expand Down Expand Up @@ -137,6 +139,7 @@ impl Language {
"graphql" | "gql" => Some(Self::GraphQL),
"elm" => Some(Self::Elm),
"rescript" => Some(Self::ReScript),
"xml" => Some(Self::Xml),
_ => None,
}
}
Expand Down Expand Up @@ -181,6 +184,7 @@ impl Language {
Self::Blade => ".blade.php",
Self::OCaml => ".ml",
Self::ReScript => ".res",
Self::Xml => ".xml",
}
}
}
Expand Down
108 changes: 108 additions & 0 deletions src/languages/xml.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use schemars::JsonSchema;

use crate::formatters::{xmllint::format_using_xmllint, MdsfFormatter};

use super::{Lang, LanguageFormatter};

#[derive(Debug, Default, serde::Serialize, serde::Deserialize, JsonSchema)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub enum Xml {
#[default]
#[serde(rename = "xmllint")]
Xmllint,
}

impl Default for Lang<Xml> {
#[inline]
fn default() -> Self {
Self {
enabled: true,
formatter: MdsfFormatter::<Xml>::default(),
}
}
}

impl Default for MdsfFormatter<Xml> {
#[inline]
fn default() -> Self {
Self::Single(Xml::Xmllint)
}
}

impl LanguageFormatter for Xml {
#[inline]
fn format_snippet(
&self,
snippet_path: &std::path::Path,
) -> std::io::Result<(bool, Option<String>)> {
match self {
Self::Xmllint => format_using_xmllint(snippet_path),
}
}
}

#[cfg(test)]
mod test_xml {
use crate::{
formatters::{setup_snippet, MdsfFormatter},
languages::Lang,
};

use super::Xml;

const INPUT: &str = "
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>";

const EXTENSION: &str = crate::languages::Language::Xml.to_file_ext();

#[test]
fn it_should_be_enabled_by_default() {
assert!(Lang::<Xml>::default().enabled);
}

#[test]
fn it_should_not_format_when_enabled_is_false() {
let l = Lang::<Xml> {
enabled: false,
formatter: MdsfFormatter::Single(Xml::Xmllint),
};

let snippet = setup_snippet(INPUT, EXTENSION).expect("it to save the file");
let snippet_path = snippet.path();

assert!(l.format(snippet_path).expect("it to not fail").is_none());
}

#[test_with::executable(xmllint)]
#[test]
fn test_xmllint() {
let l = Lang::<Xml> {
enabled: true,
formatter: MdsfFormatter::Single(Xml::Xmllint),
};

let snippet = setup_snippet(INPUT, EXTENSION).expect("it to save the file");
let snippet_path = snippet.path();

let output = l
.format(snippet_path)
.expect("it to not fail")
.expect("it to be a snippet");

let expected_output = r#"<?xml version="1.0"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
"#;

assert_eq!(output, expected_output);
}
}

0 comments on commit 2b335e3

Please sign in to comment.