w3c_validators/w3c_validators/
css_validator.rs

1extern crate serde_json;
2
3use std::default::Default;
4use std::time::Duration;
5
6use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
7use reqwest::multipart::Form as MultipartForm;
8use reqwest::Client as HttpClient;
9use reqwest::{Response, StatusCode};
10
11pub const CSS_VALIDATOR_URI: &str = "https://jigsaw.w3.org/css-validator/validator";
12
13#[derive(Serialize, Deserialize)]
14pub struct CssResult {
15    pub errorcount: u8,
16    pub warningcount: u8,
17}
18
19#[derive(Serialize, Deserialize)]
20pub struct CssValidation {
21    pub uri: String,
22    pub checkedby: String,
23    pub csslevel: String,
24    pub date: String,
25    pub timestamp: String,
26    pub validity: bool,
27    pub result: CssResult,
28    pub warnings: Option<Vec<Message>>,
29    pub errors: Option<Vec<Message>>,
30}
31
32#[derive(Serialize, Deserialize)]
33pub struct Message {
34    pub source: String,
35    pub message: String,
36    pub line: u8,
37    #[serde(rename = "type")]
38    pub _type: String,
39    pub level: Option<u8>,
40}
41
42#[derive(Serialize, Deserialize)]
43pub struct CssValidatorResult {
44    pub cssvalidation: CssValidation,
45}
46
47pub struct CssValidatorOpts {
48    validator_uri: String,
49}
50
51impl CssValidation {
52    pub fn is_valid(&self) -> bool {
53        match self.errors {
54            Some(ref err) => err.is_empty(),
55            None => true,
56        }
57    }
58}
59
60impl CssValidatorResult {
61    pub fn is_valid(&self) -> bool {
62        self.cssvalidation.is_valid()
63    }
64}
65
66impl Default for CssValidatorOpts {
67    fn default() -> CssValidatorOpts {
68        CssValidatorOpts {
69            validator_uri: CSS_VALIDATOR_URI.to_string(),
70        }
71    }
72}
73
74pub struct CssValidator {
75    pub validator_uri: String,
76    http_client: HttpClient,
77}
78
79/// # Examples
80///
81/// ```
82/// use std::default::Default;
83/// use w3c_validators::CssValidator;
84/// let nu = CssValidator::new(Default::default());
85/// match nu.validate_text("tbody th{width: /* 25%} */") {
86///     Some(result) => {
87///         if !result.is_valid() {
88///             for msg in result.cssvalidation.errors.unwrap() {
89///                 println!("{}: {}", msg._type, msg.message);
90///             }
91///         }
92///     }
93///     None => {},
94/// }
95/// ```
96impl CssValidator {
97    pub fn new(opts: CssValidatorOpts) -> CssValidator {
98        let http_client = HttpClient::builder()
99            .timeout(Duration::from_secs(5))
100            .build()
101            .expect("HttpClient failed to construct");
102        CssValidator {
103            validator_uri: opts.validator_uri,
104            http_client,
105        }
106    }
107
108    fn fetch_result(&self, mut response: Response) -> Option<CssValidatorResult> {
109        match response.text() {
110            Ok(data) => match serde_json::from_str(&data) {
111                Ok(v) => Some(v),
112                _ => None,
113            },
114            _ => None,
115        }
116    }
117
118    pub fn validate_uri(&self, uri: &str) -> Option<CssValidatorResult> {
119        let url = &format!("{}?uri={}&output=json", CSS_VALIDATOR_URI, uri);
120        match self
121            .http_client
122            .get(url)
123            .header(USER_AGENT, ua_validator!())
124            .send()
125        {
126            Err(_) => None,
127            Ok(response) => match response.status() {
128                StatusCode::OK | StatusCode::NOT_MODIFIED => self.fetch_result(response),
129                _ => None,
130            },
131        }
132    }
133
134    pub fn validate_text(&self, text: &'static str) -> Option<CssValidatorResult> {
135        let mut headers = HeaderMap::new();
136        headers.insert(USER_AGENT, HeaderValue::from_static(ua_validator!()));
137        let form = MultipartForm::new()
138            .text("text", text)
139            .text("output", "json");
140        match self
141            .http_client
142            .post(CSS_VALIDATOR_URI)
143            .multipart(form)
144            .headers(headers)
145            .send()
146        {
147            Err(_) => None,
148            Ok(response) => match response.status() {
149                StatusCode::OK | StatusCode::NOT_MODIFIED => self.fetch_result(response),
150                _ => None,
151            },
152        }
153    }
154}