singbox_driver/codec/
profile_document_codec.rs1use lurker_core_contract::identity::EngineId;
4use lurker_core_contract::profile_document::{DocumentDiagnosticSnapshot, DocumentValidation};
5use lurker_core_contract::profile_document_identity::{ProfileFormatId, ProfileSchemaVersion};
6use lurker_core_ports::error::PortError;
7use lurker_core_ports::profile_document_codec::{
8 ProfileDocumentCodecPort, ProfileDocumentCodecScope,
9};
10
11pub const PROFILE_FORMAT: &str = "sing-box-json";
13pub const PROFILE_SCHEMA_VERSION: u32 = 1;
15
16#[derive(Clone, Copy, Debug, Default)]
18pub struct SingBoxProfileDocumentCodec;
19
20impl ProfileDocumentCodecPort for SingBoxProfileDocumentCodec {
21 fn scope(&self) -> ProfileDocumentCodecScope {
22 ProfileDocumentCodecScope {
23 engine_id: EngineId::sing_box(),
24 format_id: ProfileFormatId::new(PROFILE_FORMAT)
25 .expect("static sing-box format ID is valid"),
26 schema_version: ProfileSchemaVersion::new(PROFILE_SCHEMA_VERSION)
27 .expect("sing-box schema version is positive"),
28 }
29 }
30
31 fn inspect(&self, raw_bytes: &[u8]) -> Result<DocumentValidation, PortError> {
32 let value: serde_json::Value = match serde_json::from_slice(raw_bytes) {
33 Ok(value) => value,
34 Err(error) => {
35 return Ok(DocumentValidation::Invalid {
36 diagnostics: vec![DocumentDiagnosticSnapshot {
37 line: Some(error.line()),
38 column: Some(error.column()),
39 message: "invalid sing-box JSON syntax".to_owned(),
40 }],
41 });
42 }
43 };
44
45 if value.is_object() {
46 Ok(DocumentValidation::Valid)
47 } else {
48 Ok(DocumentValidation::Invalid {
49 diagnostics: vec![DocumentDiagnosticSnapshot {
50 line: None,
51 column: None,
52 message: "sing-box configuration root must be a JSON object".to_owned(),
53 }],
54 })
55 }
56 }
57}
58
59#[cfg(test)]
60#[path = "profile_document_codec_tests.rs"]
61mod tests;