<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Debug;
use std::io::Read;
use std::rc::Rc;

use reqwest;
use reqwest::header::{Authorization, Bearer, Headers};
use serde_json;
use url;

use super::error::{TvdbError, TvdbResult};
use data::{EpisodeId, SeriesId};

/// Trait for custom implementations of URL fetching
pub trait RequestClient: Debug {
    fn get_url(&self, url: &str, jwt_token: Option<String>) -> TvdbResult<String>;
}

/// Default implementation of RequestClient
#[derive(Debug)]
pub struct DefaultHttpClient;

impl RequestClient for DefaultHttpClient {
    fn get_url(&self, url: &str, jwt_token: Option<String>) -> TvdbResult<String> {
        // Make request
        let client = reqwest::Client::new();

        let mut headers = Headers::new();
        if let Some(tok) = jwt_token {
            headers.set(Authorization(Bearer { token: tok.into() }));
        }

        let mut resp = client.get(url).headers(headers).send().map_err(|x| {
            TvdbError::CommunicationError {
                reason: format!("Error creating HTTP request: {}", x),
            }
        })?;

        // Check response
        if !resp.status().is_success() {
            return Err(TvdbError::CommunicationError {
                reason: format!(
                    "Unsuccessful HTTP response from url {}: {}",
                    url,
                    resp.status()
                ),
            });
        }

        let mut result = String::new();
        resp.read_to_string(&mut result)
            .map_err(|x| TvdbError::CommunicationError {
                reason: format!("Error reading response: {}", x),
            })?;
        return Ok(result);
    }
}

/// Main interface
#[derive(Debug, Clone)]
pub struct Tvdb<'a> {
    /// Your API key from TheTVDB.com
    pub key: String,
    http_client: Option<&'a RequestClient>,
    jwt_token: RefCell<Option<String>>,
    default_client: Rc<RequestClient>,
}

/// https://api.thetvdb.com/swagger#/Authentication
#[derive(Deserialize, Debug)]
struct LoginResponse {
    token: String,
}

/// List of `SeriesSearchData`, returned from a search
#[derive(Deserialize, Debug)]
pub struct SeriesSearchResult {
    pub data: Option<Vec<SeriesSearchData>>,
    pub error: Option<String>,
}

/// Info for a single series, as returned from search query
#[derive(Deserialize, Debug, Clone)]
pub struct SeriesSearchData {
    pub aliases: Option<Vec<String>>,
    pub banner: Option<String>,
    #[serde(rename = "firstAired")]
    pub first_aired: Option<String>,
    pub id: Option<u32>,
    pub network: Option<String>,
    pub overview: Option<String>,
    #[serde(rename = "seriesName")]
    pub series_name: String,
    pub status: Option<String>,
}

impl From<SeriesSearchData> for EpisodeId {
    fn from(x: SeriesSearchData) -> Self {
        EpisodeId {
            seriesid: x.id.unwrap() as u32,
            language: "en".into(),
        }
    }
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct JSONErrors {
    pub invalid_filters: Option<Vec<String>>,
    pub invalid_language: Option<Vec<String>>,
    pub invalid_query_params: Option<Vec<String>>,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct EpisodeRecordData {
    data: Option<Episode>,
    errors: Option<JSONErrors>,
}

/// Info for an episode
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Episode {
    pub absolute_number: Option<u32>,
    pub aired_episode_number: Option<u32>,
    pub aired_season: Option<u32>,
    pub airs_after_season: Option<u32>,
    pub airs_before_episode: Option<u32>,
    pub airs_before_season: Option<u32>,
    pub director: Option<String>,
    pub directors: Option<Vec<String>>,
    pub dvd_chapter: Option<u32>,
    pub dvd_discid: Option<String>,
    pub dvd_episode_number: Option<u32>,
    pub dvd_season: Option<u32>,
    pub episode_name: String, // FIXME: Should be optional
    pub filename: Option<String>,
    pub first_aired: Option<String>,
    pub guest_stars: Option<Vec<String>>,
    pub id: Option<u32>,
    pub imdb_id: Option<String>,
    pub last_updated: Option<u32>,
    pub last_updated_by: Option<u32>, // FIXME: Should be String
    pub overview: Option<String>,
    pub production_code: Option<String>,
    pub series_id: Option<u32>,
    pub show_url: Option<String>,
    pub site_rating: Option<f32>,
    pub site_rating_count: Option<u32>,
    pub thumb_added: Option<String>,
    pub thumb_author: Option<u32>, // FIXME: Should be String
    pub thumb_height: Option<String>,
    pub thumb_width: Option<String>,
    pub writers: Option<Vec<String>>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct SeriesEpisodes {
    pub data: Option<Vec<BasicEpisode>>,
    pub errors: Option<JSONErrors>,
    pub links: Option<Links>,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct BasicEpisode {
    pub absolute_number: Option<u32>,
    pub aired_episode_number: Option<u32>,
    pub aired_season: Option<u32>,
    pub dvd_episode_number: Option<u32>,
    pub dvd_season: Option<u32>,
    pub episode_name: Option<String>,
    pub first_aired: Option<String>,
    pub id: Option<u32>,
    pub last_updated: Option<u32>,
    pub overview: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Links {
    pub first: Option<u32>,
    pub last: Option<u32>,
    pub next: Option<u32>,
    pub previous: Option<u32>,
}

impl<'a> Tvdb<'a> {
    /// Initalise API with the given API key. A key can be acquired via
    /// the [API Key Registration page](http://thetvdb.com/?tab=apiregister)
    pub fn new<S>(key: S) -> Tvdb<'a>
    where
        S: Into<String>,
    {
        Tvdb {
            key: key.into(),
            http_client: None,
            jwt_token: RefCell::new(None),
            default_client: Rc::new(DefaultHttpClient {}),
        }
    }

    /// Set the JWT session token
    fn set_token(&self, token: String) {
        let mut j = self.jwt_token.borrow_mut();
        *j = Some(token);
    }

    /// Get JWT session token (typically set via `login` method)
    fn get_token(&self) -> Option<String> {
        let j = self.jwt_token.borrow();
        match *j {
            None => None,
            Some(ref t) => Some(format!("{}", *t)),
        }
    }

    /// Authenticate with TheTVDB, storing the JWT token internally for use by
    /// other methods.
    pub fn login(&self) -> TvdbResult<bool> {
        let mut map = HashMap::new();
        map.insert("apikey", &self.key);

        let c = reqwest::Client::new();
        let mut resp = c.post("https://api.thetvdb.com/login")
            .json(&map)
            .send()
            .map_err(|x| TvdbError::CommunicationError {
                reason: format!("{}", x),
            })?;
        let mut result = String::new();
        resp.read_to_string(&mut result)
            .map_err(|x| TvdbError::CommunicationError {
                reason: format!("Error reading response: {}", x),
            })?;

        let deserialized: serde_json::Value = serde_json::from_str(&result).unwrap();
        let tok: String = deserialized["token"].as_str().unwrap().into();
        self.set_token(tok);

        Ok(true)
    }

    /// Sets a custom client (implementation of `RequestClient`) used to
    /// perform HTTP requests
    pub fn set_http_client(&mut self, client: &'a RequestClient) {
        self.http_client = Some::<&'a RequestClient>(client);
    }

    /// Search for series by name or IMDB ID
    /// <https://api.thetvdb.com/swagger#!/Search/get_search_series>
    pub fn search(
        &self,
        name: Option<&str>,
        imdb_id: Option<&str>,
    ) -> TvdbResult<SeriesSearchResult> {
        let dc = self.default_client.as_ref();
        let c = self.http_client.unwrap_or(dc);

        let mut params: HashMap<&str, &str> = HashMap::new();
        if let Some(n) = name {
            params.insert("name", n);
        }
        if let Some(i) = imdb_id {
            params.insert("imdbId", i);
        }

        let search_url = "https://api.thetvdb.com/search/series";
        let url: String = url::Url::parse_with_params(search_url, params)
            .unwrap()
            .as_str()
            .into();
        // Query URL
        let data = c.get_url(&url, self.get_token())?;

        // Parse result
        let result: SeriesSearchResult = serde_json::from_str(&data).unwrap();

        Ok(result)
    }

    fn episode_inner(&self, id: EpisodeId) -> TvdbResult<Episode> {
        let dc = self.default_client.as_ref();
        let c = self.http_client.unwrap_or(dc);

        // TODO Use `id.language`

        let url = format!("https://api.thetvdb.com/episodes/{id}", id = id.seriesid);
        let data = c.get_url(&url, self.get_token())?;
        // Parse result
        println!("{}", data);
        let result: Result<EpisodeRecordData, serde_json::Error> = serde_json::from_str(&data);
        match result {
            Ok(r) => Ok(r.data.unwrap()),
            Err(e) => Err(TvdbError::DataError {
                reason: e.to_string(),
            }),
        }
    }

    /// Full information about given episode
    /// <https://api.thetvdb.com/swagger#!/Episodes/get_episodes_id>
    pub fn episode<T: Into<EpisodeId>>(&self, id: T) -> TvdbResult<Episode> {
        self.episode_inner(id.into())
    }

    fn series_episodes_inner(&self, id: SeriesId, page: u32) -> TvdbResult<SeriesEpisodes> {
        let dc = self.default_client.as_ref();
        let c = self.http_client.unwrap_or(dc);

        // TODO Use `id.language`

        let url = format!(
            "https://api.thetvdb.com/series/{id}/episodes?page={page}",
            id = id.seriesid,
            page = page
        );
        let data = c.get_url(&url, self.get_token())?;
        // Parse result
        let result: Result<SeriesEpisodes, serde_json::Error> = serde_json::from_str(&data);
        match result {
            Ok(r) => Ok(r),
            Err(e) => Err(TvdbError::DataError {
                reason: e.to_string(),
            }),
        }
    }

    /// All episodes for given series
    pub fn series_episodes<T: Into<SeriesId>>(
        &self,
        id: T,
        page: u32,
    ) -> TvdbResult<SeriesEpisodes> {
        self.series_episodes_inner(id.into(), page)
    }
}
<commit_msg>Use rename_all<commit_after>use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Debug;
use std::io::Read;
use std::rc::Rc;

use reqwest;
use reqwest::header::{Authorization, Bearer, Headers};
use serde_json;
use url;

use super::error::{TvdbError, TvdbResult};
use data::{EpisodeId, SeriesId};

/// Trait for custom implementations of URL fetching
pub trait RequestClient: Debug {
    fn get_url(&self, url: &str, jwt_token: Option<String>) -> TvdbResult<String>;
}

/// Default implementation of RequestClient
#[derive(Debug)]
pub struct DefaultHttpClient;

impl RequestClient for DefaultHttpClient {
    fn get_url(&self, url: &str, jwt_token: Option<String>) -> TvdbResult<String> {
        // Make request
        let client = reqwest::Client::new();

        let mut headers = Headers::new();
        if let Some(tok) = jwt_token {
            headers.set(Authorization(Bearer { token: tok.into() }));
        }

        let mut resp = client.get(url).headers(headers).send().map_err(|x| {
            TvdbError::CommunicationError {
                reason: format!("Error creating HTTP request: {}", x),
            }
        })?;

        // Check response
        if !resp.status().is_success() {
            return Err(TvdbError::CommunicationError {
                reason: format!(
                    "Unsuccessful HTTP response from url {}: {}",
                    url,
                    resp.status()
                ),
            });
        }

        let mut result = String::new();
        resp.read_to_string(&mut result)
            .map_err(|x| TvdbError::CommunicationError {
                reason: format!("Error reading response: {}", x),
            })?;
        return Ok(result);
    }
}

/// Main interface
#[derive(Debug, Clone)]
pub struct Tvdb<'a> {
    /// Your API key from TheTVDB.com
    pub key: String,
    http_client: Option<&'a RequestClient>,
    jwt_token: RefCell<Option<String>>,
    default_client: Rc<RequestClient>,
}

/// https://api.thetvdb.com/swagger#/Authentication
#[derive(Deserialize, Debug)]
struct LoginResponse {
    token: String,
}

/// List of `SeriesSearchData`, returned from a search
#[derive(Deserialize, Debug)]
pub struct SeriesSearchResult {
    pub data: Option<Vec<SeriesSearchData>>,
    pub error: Option<String>,
}

/// Info for a single series, as returned from search query
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SeriesSearchData {
    pub aliases: Option<Vec<String>>,
    pub banner: Option<String>,
    pub first_aired: Option<String>,
    pub id: Option<u32>,
    pub network: Option<String>,
    pub overview: Option<String>,
    pub series_name: String,
    pub status: Option<String>,
}

impl From<SeriesSearchData> for EpisodeId {
    fn from(x: SeriesSearchData) -> Self {
        EpisodeId {
            seriesid: x.id.unwrap() as u32,
            language: "en".into(),
        }
    }
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct JSONErrors {
    pub invalid_filters: Option<Vec<String>>,
    pub invalid_language: Option<Vec<String>>,
    pub invalid_query_params: Option<Vec<String>>,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct EpisodeRecordData {
    data: Option<Episode>,
    errors: Option<JSONErrors>,
}

/// Info for an episode
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Episode {
    pub absolute_number: Option<u32>,
    pub aired_episode_number: Option<u32>,
    pub aired_season: Option<u32>,
    pub airs_after_season: Option<u32>,
    pub airs_before_episode: Option<u32>,
    pub airs_before_season: Option<u32>,
    pub director: Option<String>,
    pub directors: Option<Vec<String>>,
    pub dvd_chapter: Option<u32>,
    pub dvd_discid: Option<String>,
    pub dvd_episode_number: Option<u32>,
    pub dvd_season: Option<u32>,
    pub episode_name: String, // FIXME: Should be optional
    pub filename: Option<String>,
    pub first_aired: Option<String>,
    pub guest_stars: Option<Vec<String>>,
    pub id: Option<u32>,
    pub imdb_id: Option<String>,
    pub last_updated: Option<u32>,
    pub last_updated_by: Option<u32>, // FIXME: Should be String
    pub overview: Option<String>,
    pub production_code: Option<String>,
    pub series_id: Option<u32>,
    pub show_url: Option<String>,
    pub site_rating: Option<f32>,
    pub site_rating_count: Option<u32>,
    pub thumb_added: Option<String>,
    pub thumb_author: Option<u32>, // FIXME: Should be String
    pub thumb_height: Option<String>,
    pub thumb_width: Option<String>,
    pub writers: Option<Vec<String>>,
}

#[derive(Deserialize, Debug, Clone)]
pub struct SeriesEpisodes {
    pub data: Option<Vec<BasicEpisode>>,
    pub errors: Option<JSONErrors>,
    pub links: Option<Links>,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct BasicEpisode {
    pub absolute_number: Option<u32>,
    pub aired_episode_number: Option<u32>,
    pub aired_season: Option<u32>,
    pub dvd_episode_number: Option<u32>,
    pub dvd_season: Option<u32>,
    pub episode_name: Option<String>,
    pub first_aired: Option<String>,
    pub id: Option<u32>,
    pub last_updated: Option<u32>,
    pub overview: Option<String>,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Links {
    pub first: Option<u32>,
    pub last: Option<u32>,
    pub next: Option<u32>,
    pub previous: Option<u32>,
}

impl<'a> Tvdb<'a> {
    /// Initalise API with the given API key. A key can be acquired via
    /// the [API Key Registration page](http://thetvdb.com/?tab=apiregister)
    pub fn new<S>(key: S) -> Tvdb<'a>
    where
        S: Into<String>,
    {
        Tvdb {
            key: key.into(),
            http_client: None,
            jwt_token: RefCell::new(None),
            default_client: Rc::new(DefaultHttpClient {}),
        }
    }

    /// Set the JWT session token
    fn set_token(&self, token: String) {
        let mut j = self.jwt_token.borrow_mut();
        *j = Some(token);
    }

    /// Get JWT session token (typically set via `login` method)
    fn get_token(&self) -> Option<String> {
        let j = self.jwt_token.borrow();
        match *j {
            None => None,
            Some(ref t) => Some(format!("{}", *t)),
        }
    }

    /// Authenticate with TheTVDB, storing the JWT token internally for use by
    /// other methods.
    pub fn login(&self) -> TvdbResult<bool> {
        let mut map = HashMap::new();
        map.insert("apikey", &self.key);

        let c = reqwest::Client::new();
        let mut resp = c.post("https://api.thetvdb.com/login")
            .json(&map)
            .send()
            .map_err(|x| TvdbError::CommunicationError {
                reason: format!("{}", x),
            })?;
        let mut result = String::new();
        resp.read_to_string(&mut result)
            .map_err(|x| TvdbError::CommunicationError {
                reason: format!("Error reading response: {}", x),
            })?;

        let deserialized: serde_json::Value = serde_json::from_str(&result).unwrap();
        let tok: String = deserialized["token"].as_str().unwrap().into();
        self.set_token(tok);

        Ok(true)
    }

    /// Sets a custom client (implementation of `RequestClient`) used to
    /// perform HTTP requests
    pub fn set_http_client(&mut self, client: &'a RequestClient) {
        self.http_client = Some::<&'a RequestClient>(client);
    }

    /// Search for series by name or IMDB ID
    /// <https://api.thetvdb.com/swagger#!/Search/get_search_series>
    pub fn search(
        &self,
        name: Option<&str>,
        imdb_id: Option<&str>,
    ) -> TvdbResult<SeriesSearchResult> {
        let dc = self.default_client.as_ref();
        let c = self.http_client.unwrap_or(dc);

        let mut params: HashMap<&str, &str> = HashMap::new();
        if let Some(n) = name {
            params.insert("name", n);
        }
        if let Some(i) = imdb_id {
            params.insert("imdbId", i);
        }

        let search_url = "https://api.thetvdb.com/search/series";
        let url: String = url::Url::parse_with_params(search_url, params)
            .unwrap()
            .as_str()
            .into();
        // Query URL
        let data = c.get_url(&url, self.get_token())?;

        // Parse result
        let result: SeriesSearchResult = serde_json::from_str(&data).unwrap();

        Ok(result)
    }

    fn episode_inner(&self, id: EpisodeId) -> TvdbResult<Episode> {
        let dc = self.default_client.as_ref();
        let c = self.http_client.unwrap_or(dc);

        // TODO Use `id.language`

        let url = format!("https://api.thetvdb.com/episodes/{id}", id = id.seriesid);
        let data = c.get_url(&url, self.get_token())?;
        // Parse result
        println!("{}", data);
        let result: Result<EpisodeRecordData, serde_json::Error> = serde_json::from_str(&data);
        match result {
            Ok(r) => Ok(r.data.unwrap()),
            Err(e) => Err(TvdbError::DataError {
                reason: e.to_string(),
            }),
        }
    }

    /// Full information about given episode
    /// <https://api.thetvdb.com/swagger#!/Episodes/get_episodes_id>
    pub fn episode<T: Into<EpisodeId>>(&self, id: T) -> TvdbResult<Episode> {
        self.episode_inner(id.into())
    }

    fn series_episodes_inner(&self, id: SeriesId, page: u32) -> TvdbResult<SeriesEpisodes> {
        let dc = self.default_client.as_ref();
        let c = self.http_client.unwrap_or(dc);

        // TODO Use `id.language`

        let url = format!(
            "https://api.thetvdb.com/series/{id}/episodes?page={page}",
            id = id.seriesid,
            page = page
        );
        let data = c.get_url(&url, self.get_token())?;
        // Parse result
        let result: Result<SeriesEpisodes, serde_json::Error> = serde_json::from_str(&data);
        match result {
            Ok(r) => Ok(r),
            Err(e) => Err(TvdbError::DataError {
                reason: e.to_string(),
            }),
        }
    }

    /// All episodes for given series
    pub fn series_episodes<T: Into<SeriesId>>(
        &self,
        id: T,
        page: u32,
    ) -> TvdbResult<SeriesEpisodes> {
        self.series_episodes_inner(id.into(), page)
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use std::env;

use ArgMatches;
use Arg;
use args::OptArg;
use args::FlagArg;
use args::PosArg;

pub struct App {
	pub name: &'static str,
	pub author: Option<&'static str>,
	pub version: Option<&'static str>,
	pub about: Option<&'static str>,
	// raw_args: Vec<Arg>,
	flags: Vec<FlagArg>,
	opts: Vec<OptArg>,
	positionals: Vec<PosArg>,
	needs_long_help: bool,
	needs_long_version: bool,
	needs_short_help: bool,
	needs_short_version: bool
}

impl App {
	pub fn new(n: &'static str) -> App {
		App {
			name: n,
			author: None,
			about: None,
			version: None,
			// raw_args: vec![],
			flags: vec![],
			opts: vec![],
			needs_long_version: true,
			needs_long_help: true,
			positionals: vec![],
			needs_short_help: true,
			needs_short_version: true
		}
	}

	pub fn author(&mut self, a: &'static str) -> &mut App {
		self.author = Some(a);
		self
	}

	pub fn about(&mut self, a: &'static str) -> &mut App {
		self.about = Some(a);
		self
	}

	pub fn version(&mut self, v: &'static str)-> &mut App  {
		self.version = Some(v);
		self
	}

	pub fn arg(&mut self, a: &Arg) -> &mut App {
		if let Some(i) = a.index {
			self.positionals.push(PosArg {
				name: a.name,
				index: i,
				required: a.required,
				help: a.help,
				value: None

			});
		} else if a.takes_value {
			self.opts.push(OptArg {
				name: a.name,
				short: a.short,
				long: a.long,
				help: a.help,
				required: a.required,
				value: None
			});
		} else {
			if let Some(l) = a.long {
				if l == "help" {
					self.needs_long_help = false;
				} else if l == "version" {
					self.needs_long_version = false;
				}
			}
			if let Some(s) = a.short {
				if s == 'h' {
					self.needs_short_help = false;
				} else if s == 'v' {
					self.needs_short_version = false;
				}
			}
			self.flags.push(FlagArg{
				name: a.name,
				short: a.short,
				long: a.long,
				help: a.help,
				multiple: a.multiple,
				occurrences: 1
			});
		}
		self
	}

	fn print_help(&self) {
		println!("Help info!");
		unsafe { libc::exit(0); }
	}

	fn print_version(&self) {
		let ver = match self.version {
			Some(v) => v,
			None => ""
		};
		println!("{} {}", self.name, ver);
		unsafe { libc::exit(0); }
	}

	fn parse_long_arg(&self, matches: &mut ArgMatches, arg: &str) -> &'static str {
		let mut p_arg = arg.trim_left_matches(|c| c == '-');
		let mut found = false;
		let mut needs_val = false;

		if p_arg == "help" && self.needs_long_help {
			self.print_help();
		} else if p_arg == "version" && self.needs_long_version {
			self.print_version();
		}
		for f in self.flags.iter() {
			if let Some(l) = f.long {
				if l == p_arg {
					found = true;
					let mut mult = false;
					for ff in matches.flags.iter_mut() {
						if ff.name == f.name {
							// already in matches
							ff.occurrences = if ff.multiple { ff.occurrences + 1 } else { 1 };
							mult = true;
						}
					}
					if ! mult {
						matches.flags.push(f.clone())
					}
					return "";
				}
			}
		}
		if p_arg.contains("=") {
			let p_argv: Vec<&str> = p_arg.split_str("=").collect();
			p_arg = p_argv[0];
			for o in self.opts.iter() {
				if let Some(l) = o.long {
					if l == p_arg {
						found = true;
						matches.opts.push(OptArg{
							name: o.name,
						    short: o.short,
						    long: o.long,
						    help: o.help,
						    required: o.required,
						    value: Some(p_argv[1].to_string())
						});
						break;
					}
				}
			}
		} else {
			for o in self.opts.iter() {
				if let Some(l) = o.long {
					if l == p_arg {
						return o.name;
					}
				}
			}
		}

		// Fails if argument supplied to binary isn't valid
		assert!(found == false);

		""
	}

	fn parse_short_arg(&self, matches: &mut ArgMatches, arg: &str) -> &'static str {
		let mut found = false;
		if arg.len() > 2 {
			// Multiple flags using short i.e. -bgHlS
			let p_arg = arg.trim_left_matches(|c| c == '-');
			for c in p_arg.chars() {
				if c == 'h' && self.needs_short_help {
					self.print_help();
				} else if c == 'v' && self.needs_short_version {
					self.print_version();
				}
				for f in self.flags.iter() {
					if let Some(s) = f.short {
						if s == c {
							found = true;
							let mut mult = false;
							for ff in matches.flags.iter_mut() {
								if ff.name == f.name {
									// already in matches
									ff.occurrences = if ff.multiple { ff.occurrences + 1 } else { 1 };
									mult = true;
								}
							}
							if ! mult {
								matches.flags.push(f.clone())
							}
							return "";
						}
					}
				}
				// Fails if argument supplied to binary isn't valid
				assert!(found == false);
				return "";
			}
		} else {
			// Short flag or opt
			let p_arg = arg.char_at(1);
			if p_arg == 'h' && self.needs_short_help {
				self.print_help();
			} else if p_arg == 'v' && self.needs_short_version {
				self.print_version();
			}
			for f in self.flags.iter() {
				if let Some(s) = f.short {
					if p_arg == s {
						found = true;
						matches.flags.push(f.clone());
					}
				}
			}
			for o in self.opts.iter() {
				if let Some(s) = o.short {
					if s == p_arg {
						return o.name;
					}
				}
			}
		}
		// Fails if argument supplied to binary isn't valid
		assert!(found == false);

		""
	}

	pub fn get_matches(&mut self) -> ArgMatches {

		let mut matches = ArgMatches::new(self);

		// let mut needs_val = false;
		let mut needs_val_of = "".to_string();
		let mut pos_counter = 1;
		for arg in env::args().collect::<Vec<String>>().tail() {
			let arg_slice = arg.as_slice();
			if ! needs_val_of.is_empty() {
				for o in self.opts.iter() {
					if needs_val_of == o.name.to_string() {
						matches.opts.push(OptArg{
							name: o.name,
						    short: o.short,
						    long: o.long,
						    help: o.help,
						    required: o.required,
						    value: Some(arg.clone())
						});
						needs_val_of.clear();
						break;
					}
				}
				continue;
			}
			if arg_slice.starts_with("--") {
				// Single flag, or option
				needs_val_of = self.parse_long_arg(&mut matches, arg_slice).to_string();

			} else if arg_slice.starts_with("-") {
				needs_val_of = self.parse_short_arg(&mut matches, arg_slice).to_string();
			} else {
				// Positional

				// Fails if no positionals are expected/possible
				assert!(self.positionals.is_empty() == false);

				for p in self.positionals.iter() {
					matches.positionals.push(PosArg{
						name: p.name,
						help: p.help,
						required: p.required,
						value: Some(arg.clone()),
						index: pos_counter
					});
					pos_counter += 1;
				}
			}

		}

		// Fails if we reached the end of args() but were still
		// expecting a value, such as ./fake -c
		// where -c takes a value
		assert!(needs_val_of.is_empty());

		matches
	}
}<commit_msg>v0.0.4.4<commit_after>extern crate libc;

use std::env;

use ArgMatches;
use Arg;
use args::OptArg;
use args::FlagArg;
use args::PosArg;

pub struct App {
	pub name: &'static str,
	pub author: Option<&'static str>,
	pub version: Option<&'static str>,
	pub about: Option<&'static str>,
	// raw_args: Vec<Arg>,
	flags: Vec<FlagArg>,
	opts: Vec<OptArg>,
	positionals: Vec<PosArg>,
	needs_long_help: bool,
	needs_long_version: bool,
	needs_short_help: bool,
	needs_short_version: bool
}

impl App {
	pub fn new(n: &'static str) -> App {
		App {
			name: n,
			author: None,
			about: None,
			version: None,
			// raw_args: vec![],
			flags: vec![],
			opts: vec![],
			needs_long_version: true,
			needs_long_help: true,
			positionals: vec![],
			needs_short_help: true,
			needs_short_version: true
		}
	}

	pub fn author(&mut self, a: &'static str) -> &mut App {
		self.author = Some(a);
		self
	}

	pub fn about(&mut self, a: &'static str) -> &mut App {
		self.about = Some(a);
		self
	}

	pub fn version(&mut self, v: &'static str)-> &mut App  {
		self.version = Some(v);
		self
	}

	pub fn arg(&mut self, a: &Arg) -> &mut App {
		if let Some(i) = a.index {
			self.positionals.push(PosArg {
				name: a.name,
				index: i,
				required: a.required,
				help: a.help,
				value: None

			});
		} else if a.takes_value {
			self.opts.push(OptArg {
				name: a.name,
				short: a.short,
				long: a.long,
				help: a.help,
				required: a.required,
				value: None
			});
		} else {
			if let Some(l) = a.long {
				if l == "help" {
					self.needs_long_help = false;
				} else if l == "version" {
					self.needs_long_version = false;
				}
			}
			if let Some(s) = a.short {
				if s == 'h' {
					self.needs_short_help = false;
				} else if s == 'v' {
					self.needs_short_version = false;
				}
			}
			self.flags.push(FlagArg{
				name: a.name,
				short: a.short,
				long: a.long,
				help: a.help,
				multiple: a.multiple,
				occurrences: 1
			});
		}
		self
	}

	fn print_help(&self) {
		println!("Help info!");
		unsafe { libc::exit(0); }
	}

	fn print_version(&self) {
		let ver = match self.version {
			Some(v) => v,
			None => ""
		};
		println!("{} {}", self.name, ver);
		unsafe { libc::exit(0); }
	}

	fn validate_short_flag(&self, matches: &mut ArgMatches, arg: char) -> bool {
		let mut found = false;
		for f in self.flags.iter() {
			if let Some(s) = f.short {
				if s == arg {
					found = true;
					let mut mult = false;
					for ff in matches.flags.iter_mut() {
						if ff.name == f.name {
							// already in matches
							ff.occurrences = if ff.multiple { ff.occurrences + 1 } else { 1 };
							mult = true;
							break;
						}
					}
					if ! mult {
						matches.flags.push(f.clone())
					}
				}
				break;
			}
		}
		found
	}

	fn parse_long_arg(&self, matches: &mut ArgMatches, arg: &str) -> &'static str {
		let mut p_arg = arg.trim_left_matches(|c| c == '-');
		let mut found = false;
		let mut needs_val = false;

		if p_arg == "help" && self.needs_long_help {
			self.print_help();
		} else if p_arg == "version" && self.needs_long_version {
			self.print_version();
		}
		for f in self.flags.iter() {
			if let Some(l) = f.long {
				if l == p_arg {
					found = true;
					let mut mult = false;
					for ff in matches.flags.iter_mut() {
						if ff.name == f.name {
							// already in matches
							ff.occurrences = if ff.multiple { ff.occurrences + 1 } else { 1 };
							mult = true;
						}
					}
					if ! mult {
						matches.flags.push(f.clone())
					}
					return "";
				}
			}
		}
		if p_arg.contains("=") {
			let p_argv: Vec<&str> = p_arg.split_str("=").collect();
			p_arg = p_argv[0];
			for o in self.opts.iter() {
				if let Some(l) = o.long {
					if l == p_arg {
						found = true;
						matches.opts.push(OptArg{
							name: o.name,
						    short: o.short,
						    long: o.long,
						    help: o.help,
						    required: o.required,
						    value: Some(p_argv[1].to_string())
						});
						break;
					}
				}
			}
		} else {
			for o in self.opts.iter() {
				if let Some(l) = o.long {
					if l == p_arg {
						return o.name;
					}
				}
			}
		}

		// Fails if argument supplied to binary isn't valid
		assert!(found == false);

		""
	}

	fn parse_short_arg(&self, matches: &mut ArgMatches, arg: &str) -> &'static str {
		let mut found = false;
		if arg.len() > 2 {
			// Multiple flags using short i.e. -bgHlS
			let p_arg = arg.trim_left_matches(|c| c == '-');
			for c in p_arg.chars() {
				if c == 'h' && self.needs_short_help {
					self.print_help();
				} else if c == 'v' && self.needs_short_version {
					self.print_version();
				}
				found = self.validate_short_flag(matches, c);
				// Fails if argument supplied to binary isn't valid
				assert!(found == false);
				return "";
			}
		} else {
			// Short flag or opt
			let p_arg = arg.char_at(1);
			if p_arg == 'h' && self.needs_short_help {
				self.print_help();
			} else if p_arg == 'v' && self.needs_short_version {
				self.print_version();
			}
			found = self.validate_short_flag(matches, p_arg);
			if ! found {
				for o in self.opts.iter() {
					if let Some(s) = o.short {
						if s == p_arg {
							return o.name;
						}
					}
				}
			}
		}
		// Fails if argument supplied to binary isn't valid
		assert!(found == false);

		""
	}

	pub fn get_matches(&mut self) -> ArgMatches {

		let mut matches = ArgMatches::new(self);

		// let mut needs_val = false;
		let mut needs_val_of = "".to_string();
		let mut pos_counter = 1;
		for arg in env::args().collect::<Vec<String>>().tail() {
			let arg_slice = arg.as_slice();
			if ! needs_val_of.is_empty() {
				for o in self.opts.iter() {
					if needs_val_of == o.name.to_string() {
						matches.opts.push(OptArg{
							name: o.name,
						    short: o.short,
						    long: o.long,
						    help: o.help,
						    required: o.required,
						    value: Some(arg.clone())
						});
						needs_val_of.clear();
						break;
					}
				}
				continue;
			}
			if arg_slice.starts_with("--") {
				// Single flag, or option
				needs_val_of = self.parse_long_arg(&mut matches, arg_slice).to_string();

			} else if arg_slice.starts_with("-") {
				needs_val_of = self.parse_short_arg(&mut matches, arg_slice).to_string();
			} else {
				// Positional

				// Fails if no positionals are expected/possible
				assert!(self.positionals.is_empty() == false);

				for p in self.positionals.iter() {
					matches.positionals.push(PosArg{
						name: p.name,
						help: p.help,
						required: p.required,
						value: Some(arg.clone()),
						index: pos_counter
					});
					pos_counter += 1;
				}
			}

		}

		// Fails if we reached the end of args() but were still
		// expecting a value, such as ./fake -c
		// where -c takes a value
		assert!(needs_val_of.is_empty());

		matches
	}
}<|endoftext|>


<commit_before>#![allow(dead_code)]

//! Diagnostics API.

// Right now this is implemented on top of codespan, but fixes a few things I
// don't like about the API.

use codespan;
use codespan_reporting::diagnostic as imp;
use codespan_reporting::term;
use std::{
    cell::RefCell,
    fmt::{self, Debug},
    hash::Hash,
    iter::{FromIterator, IntoIterator},
    rc::Rc,
};
use termcolor::{self, ColorChoice};

pub mod db {
    use super::*;
    use codespan_reporting::files::{Line, SimpleFile};
    use salsa::{self, InternKey};
    use std::sync::Arc;

    intern_key!(FileId);
    impl PartialOrd for FileId {
        fn partial_cmp(&self, other: &FileId) -> Option<std::cmp::Ordering> {
            Some(self.cmp(other))
        }
    }
    impl Ord for FileId {
        fn cmp(&self, other: &FileId) -> std::cmp::Ordering {
            self.as_intern_id()
                .as_usize()
                .cmp(&other.as_intern_id().as_usize())
        }
    }

    /// Since the Files trait (and libclang) copy the entire file contents every
    /// time we request them, we need a way of caching those contents. Once cached,
    /// we wrap them in SimpleFile, which creates an index of the start of every
    /// line.
    // TODO: Why am I double-layering Arc's here?
    #[derive(Debug, Clone)]
    pub struct BasicFile(SimpleFile<Arc<str>, Arc<str>>);
    impl PartialEq for BasicFile {
        fn eq(&self, other: &BasicFile) -> bool {
            Arc::ptr_eq(self.0.origin(), other.0.origin())
                && Arc::ptr_eq(self.0.source(), other.0.source())
        }
    }
    impl Eq for BasicFile {}

    /// Cache for [`BasicFile`]. Should not be used outside of the `diagnostics` module.
    #[salsa::query_group(BasicFileCacheStorage)]
    pub trait BasicFileCache: crate::FileInterner {
        fn basic_file(&self, id: FileId) -> Arc<BasicFile>;
    }
    fn basic_file(db: &impl crate::FileInterner, id: FileId) -> Arc<BasicFile> {
        let (name, contents) = crate::File::get_name_and_contents(db, id);
        Arc::new(BasicFile(SimpleFile::new(name.into(), contents.into())))
    }

    /// Adapter between salsa, BasicFile, and the Files trait.
    pub(super) struct FilesWrapper<'db, DB: BasicFileCache>(pub(super) &'db DB);
    impl<'a, DB: BasicFileCache> codespan_reporting::files::Files<'a> for FilesWrapper<'a, DB> {
        type FileId = FileId;
        type Origin = Arc<str>;
        type LineSource = String;

        fn origin(&self, id: FileId) -> Option<Self::Origin> {
            Some(self.0.basic_file(id).0.origin().clone())
        }

        fn line(&self, id: FileId, line_idx: usize) -> Option<Line<String>> {
            self.0.basic_file(id).0.line((), line_idx).map(|line| Line {
                start: line.start,
                number: line.number,
                source: line.source.to_owned(),
            })
        }

        fn line_index(&self, id: FileId, byte_idx: usize) -> Option<usize> {
            self.0.basic_file(id).0.line_index((), byte_idx)
        }
    }
}

//pub use codespan::FileId;

pub use db::FileId;

/// The source code associated with an object.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Span {
    file_id: FileId,
    span: codespan::Span,
}

impl Span {
    pub fn new(file_id: FileId, start_offset: u32, end_offset: u32) -> Self {
        Span {
            file_id,
            span: codespan::Span::new(start_offset, end_offset),
        }
    }

    pub fn label(&self, message: impl Into<String>) -> Label {
        let range = self.span.start().to_usize()..self.span.end().to_usize();
        Label(imp::Label::primary(self.file_id, range).with_message(message))
    }
}

/// A message associated with a Span.
pub struct Label(imp::Label<FileId>);

/// Creates diagnostics and keeps track of statistics for a compile session.
pub struct DiagnosticsCtx(Rc<RefCell<CtxInner>>);

struct CtxInner {
    counts: Counts,
    mode: Mode,
}

enum Mode {
    Term { writer: termcolor::StandardStream },
    Test { errs: Vec<String> },
}

type Count = u32;
#[derive(Default)]
struct Counts {
    bugs: Count,
    errors: Count,
    warns: Count,
    infos: Count,
    helps: Count,
}

impl DiagnosticsCtx {
    pub fn new() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Term {
                writer: termcolor::StandardStream::stderr(ColorChoice::Auto),
            },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    pub(crate) fn test() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Test { errs: vec![] },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    pub(crate) fn get_test(&self) -> Vec<String> {
        match &self.0.borrow().mode {
            Mode::Test { errs } => errs.clone(),
            _ => panic!("expected test mode"),
        }
    }

    pub fn has_bugs(&self) -> bool {
        // TODO: should we just panic instead?
        self.0.borrow().counts.errors > 0
    }

    pub fn has_errors(&self) -> bool {
        self.0.borrow().counts.errors > 0
    }

    fn clone(&self) -> Self {
        DiagnosticsCtx(Rc::clone(&self.0))
    }
}

#[must_use]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Diagnostic(imp::Diagnostic<db::FileId>);

impl fmt::Debug for Diagnostic {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // TODO
        write!(f, "Diagnostic({})", self.message())
    }
}

impl Diagnostic {
    pub fn bug(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::bug()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn error(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::error()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn warn(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::warning()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn info(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::note()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn help(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::help()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn emit(self, db: &'_ impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        let CtxInner { mode, counts, .. } = &mut *ctx.0.borrow_mut();
        match self.0.severity {
            imp::Severity::Bug => counts.bugs += 1,
            imp::Severity::Error => counts.errors += 1,
            imp::Severity::Warning => counts.warns += 1,
            imp::Severity::Note => (),
            imp::Severity::Help => counts.helps += 1,
        }
        match mode {
            Mode::Term { writer } => {
                term::emit(writer, &Default::default(), &db::FilesWrapper(db), &self.0)
                    .expect("failed to emit diagnostic")
            }
            Mode::Test { errs } => errs.push(self.0.message),
        }
    }

    pub fn with_note(mut self, note: impl Into<String>) -> Self {
        self.0.notes.push(note.into());
        self
    }

    pub fn with_notes(mut self, notes: impl IntoIterator<Item = String>) -> Diagnostic {
        self.0.notes.extend(notes);
        self
    }

    pub fn with_label(mut self, secondary_label: Label) -> Diagnostic {
        self.0.labels.push(secondary_label.0);
        self
    }

    pub fn with_labels(mut self, seconadry_labels: impl IntoIterator<Item = Label>) -> Diagnostic {
        self.0
            .labels
            .extend(seconadry_labels.into_iter().map(|l| l.0));
        self
    }

    pub fn message(&self) -> &str {
        &self.0.message
    }
}

/// An ordered list of diagnostics.
// TODO: panic if a Diagnostic[s] is dropped without ever being emitted
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[must_use]
pub struct Diagnostics {
    val: Vec<Diagnostic>,
}
impl Diagnostics {
    pub fn new() -> Diagnostics {
        Diagnostics { val: vec![] }
    }

    pub fn build(f: impl FnOnce(&mut Diagnostics)) -> Diagnostics {
        let mut diags = Diagnostics::new();
        f(&mut diags);
        diags
    }

    /// Adds a diagnostic.
    pub fn add(&mut self, diag: Diagnostic) {
        self.val.push(diag);
    }

    /// Combines diagnostics from `self` and `second` in order.
    ///
    /// This is useful when combining diagnostics from a query result with those
    /// from another operation, for example.
    ///
    /// The conventional way to call this method is using UFCS, e.g.
    ///
    /// ```
    /// Diagnostics::merge(&first, &second)
    /// ```
    ///
    /// but it can be useful to call as a method when chaining multiple merges.
    pub fn merge(&self, second: &Diagnostics) -> Diagnostics {
        // One day we can optimize the data structure for this case, instead of
        // cloning. We'll probably use Arcs, but optimize for the empty case.
        Diagnostics {
            val: self.val.iter().chain(second.val.iter()).cloned().collect(),
        }
    }

    /// Consumes `other`, adding all diagnostics to `self`.
    pub fn append(&mut self, mut other: Diagnostics) {
        self.val.append(&mut other.val);
    }

    pub fn emit(self, db: &impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        for diag in self.val {
            diag.emit(db, ctx);
        }
    }

    pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
        self.val.iter()
    }
}
impl From<Diagnostic> for Diagnostics {
    fn from(err: Diagnostic) -> Self {
        Diagnostics { val: vec![err] }
    }
}
impl From<Diagnostics> for Vec<Diagnostic> {
    fn from(diags: Diagnostics) -> Self {
        diags.val
    }
}

/// A value, plus any diagnostics that occurred while computing the value.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[must_use]
pub struct Outcome<T> {
    val: T,
    err: Diagnostics,
}
impl<T> Outcome<T> {
    pub fn from_ok(val: T) -> Outcome<T> {
        Outcome {
            val,
            err: Diagnostics::new(),
        }
    }

    pub fn from_err(val: T, err: Diagnostic) -> Outcome<T> {
        Outcome {
            val,
            err: err.into(),
        }
    }

    pub fn is_ok(&self) -> bool {
        self.err.val.is_empty()
    }

    pub fn as_ref<'a>(&'a self) -> RefOutcome<'a, T> {
        RefOutcome {
            val: &self.val,
            err: &self.err,
        }
    }

    // Use this when you need to bypass Arc::as_ref.
    pub fn to_ref<'a>(&'a self) -> RefOutcome<'a, T> {
        self.as_ref()
    }

    pub fn val(self) -> Result<T, Diagnostics> {
        if self.is_ok() {
            Ok(self.val)
        } else {
            Err(self.err)
        }
    }

    pub fn skip_errs(self) -> T {
        self.val
    }

    pub fn errs(self) -> Diagnostics {
        self.err
    }

    pub fn split(self) -> (T, Diagnostics) {
        (self.val, self.err)
    }

    pub fn then<R>(self, f: impl FnOnce(T) -> Outcome<R>) -> Outcome<R> {
        let outcome = f(self.val);
        Outcome {
            val: outcome.val,
            err: Diagnostics::merge(&self.err, &outcome.err),
        }
    }

    pub fn map<R>(self, f: impl FnOnce(T) -> R) -> Outcome<R> {
        Outcome {
            val: f(self.val),
            err: self.err,
        }
    }
}

pub fn ok<T>(val: T) -> Outcome<T> {
    Outcome::from_ok(val)
}

pub fn err<T>(val: T, err: Diagnostic) -> Outcome<T> {
    Outcome::from_err(val, err)
}

pub struct RefOutcome<'a, T> {
    val: &'a T,
    err: &'a Diagnostics,
}

impl<'a, T> RefOutcome<'a, T> {
    pub fn is_ok(&self) -> bool {
        self.err.val.is_empty()
    }

    pub fn val(self) -> Result<&'a T, &'a Diagnostics> {
        if self.is_ok() {
            Ok(self.val)
        } else {
            Err(self.err)
        }
    }

    pub fn skip_errs(self) -> &'a T {
        self.val
    }

    pub fn errs(self) -> &'a Diagnostics {
        self.err
    }

    pub fn split(self) -> (&'a T, &'a Diagnostics) {
        (self.val, self.err)
    }

    pub fn then<R>(self, f: impl FnOnce(&'a T) -> Outcome<R>) -> Outcome<R> {
        let outcome = f(self.val);
        Outcome {
            val: outcome.val,
            err: Diagnostics::merge(&self.err, &outcome.err),
        }
    }
}

impl<A, B> FromIterator<Outcome<A>> for Outcome<B>
where
    B: FromIterator<A>,
{
    fn from_iter<I>(iter: I) -> Self
    where
        I: IntoIterator<Item = Outcome<A>>,
    {
        let mut errs = Diagnostics::new();
        let vals = iter.into_iter().map(|oc| {
            errs.append(oc.err);
            oc.val
        });
        Outcome {
            val: B::from_iter(vals),
            err: errs,
        }
    }
}

impl<T> From<(T, Diagnostics)> for Outcome<T> {
    fn from(x: (T, Diagnostics)) -> Self {
        Outcome { val: x.0, err: x.1 }
    }
}

impl From<Diagnostics> for Outcome<()> {
    fn from(err: Diagnostics) -> Self {
        Outcome { val: (), err }
    }
}
<commit_msg>Remove blanket allow(dead_code) in diagnostics<commit_after>//! Diagnostics API.

// Right now this is implemented on top of codespan, but fixes a few things I
// don't like about the API.

use codespan;
use codespan_reporting::diagnostic as imp;
use codespan_reporting::term;
use std::{
    cell::RefCell,
    fmt::{self, Debug},
    hash::Hash,
    iter::{FromIterator, IntoIterator},
    rc::Rc,
};
use termcolor::{self, ColorChoice};

pub mod db {
    use super::*;
    use codespan_reporting::files::{Line, SimpleFile};
    use salsa::{self, InternKey};
    use std::sync::Arc;

    intern_key!(FileId);
    impl PartialOrd for FileId {
        fn partial_cmp(&self, other: &FileId) -> Option<std::cmp::Ordering> {
            Some(self.cmp(other))
        }
    }
    impl Ord for FileId {
        fn cmp(&self, other: &FileId) -> std::cmp::Ordering {
            self.as_intern_id()
                .as_usize()
                .cmp(&other.as_intern_id().as_usize())
        }
    }

    /// Since the Files trait (and libclang) copy the entire file contents every
    /// time we request them, we need a way of caching those contents. Once cached,
    /// we wrap them in SimpleFile, which creates an index of the start of every
    /// line.
    // TODO: Why am I double-layering Arc's here?
    #[derive(Debug, Clone)]
    pub struct BasicFile(SimpleFile<Arc<str>, Arc<str>>);
    impl PartialEq for BasicFile {
        fn eq(&self, other: &BasicFile) -> bool {
            Arc::ptr_eq(self.0.origin(), other.0.origin())
                && Arc::ptr_eq(self.0.source(), other.0.source())
        }
    }
    impl Eq for BasicFile {}

    /// Cache for [`BasicFile`]. Should not be used outside of the `diagnostics` module.
    #[salsa::query_group(BasicFileCacheStorage)]
    pub trait BasicFileCache: crate::FileInterner {
        fn basic_file(&self, id: FileId) -> Arc<BasicFile>;
    }
    fn basic_file(db: &impl crate::FileInterner, id: FileId) -> Arc<BasicFile> {
        let (name, contents) = crate::File::get_name_and_contents(db, id);
        Arc::new(BasicFile(SimpleFile::new(name.into(), contents.into())))
    }

    /// Adapter between salsa, BasicFile, and the Files trait.
    pub(super) struct FilesWrapper<'db, DB: BasicFileCache>(pub(super) &'db DB);
    impl<'a, DB: BasicFileCache> codespan_reporting::files::Files<'a> for FilesWrapper<'a, DB> {
        type FileId = FileId;
        type Origin = Arc<str>;
        type LineSource = String;

        fn origin(&self, id: FileId) -> Option<Self::Origin> {
            Some(self.0.basic_file(id).0.origin().clone())
        }

        fn line(&self, id: FileId, line_idx: usize) -> Option<Line<String>> {
            self.0.basic_file(id).0.line((), line_idx).map(|line| Line {
                start: line.start,
                number: line.number,
                source: line.source.to_owned(),
            })
        }

        fn line_index(&self, id: FileId, byte_idx: usize) -> Option<usize> {
            self.0.basic_file(id).0.line_index((), byte_idx)
        }
    }
}

pub use db::FileId;

/// The source code associated with an object.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Span {
    file_id: FileId,
    span: codespan::Span,
}

impl Span {
    pub fn new(file_id: FileId, start_offset: u32, end_offset: u32) -> Self {
        Span {
            file_id,
            span: codespan::Span::new(start_offset, end_offset),
        }
    }

    pub fn label(&self, message: impl Into<String>) -> Label {
        let range = self.span.start().to_usize()..self.span.end().to_usize();
        Label(imp::Label::primary(self.file_id, range).with_message(message))
    }
}

/// A message associated with a Span.
pub struct Label(imp::Label<FileId>);

/// Creates diagnostics and keeps track of statistics for a compile session.
pub struct DiagnosticsCtx(Rc<RefCell<CtxInner>>);

struct CtxInner {
    counts: Counts,
    mode: Mode,
}

enum Mode {
    Term {
        writer: termcolor::StandardStream,
    },
    #[allow(dead_code)]
    Test {
        errs: Vec<String>,
    },
}

type Count = u32;
#[derive(Default)]
struct Counts {
    bugs: Count,
    errors: Count,
    warns: Count,
    #[allow(dead_code)]
    infos: Count,
    helps: Count,
}

impl DiagnosticsCtx {
    pub fn new() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Term {
                writer: termcolor::StandardStream::stderr(ColorChoice::Auto),
            },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    pub(crate) fn test() -> Self {
        let inner = CtxInner {
            counts: Counts::default(),
            mode: Mode::Test { errs: vec![] },
        };
        DiagnosticsCtx(Rc::new(RefCell::new(inner)))
    }

    #[cfg(test)]
    #[allow(dead_code)]
    pub(crate) fn get_test(&self) -> Vec<String> {
        match &self.0.borrow().mode {
            Mode::Test { errs } => errs.clone(),
            _ => panic!("expected test mode"),
        }
    }

    pub fn has_bugs(&self) -> bool {
        // TODO: should we just panic instead?
        self.0.borrow().counts.errors > 0
    }

    pub fn has_errors(&self) -> bool {
        self.0.borrow().counts.errors > 0
    }

    #[allow(dead_code)]
    fn clone(&self) -> Self {
        DiagnosticsCtx(Rc::clone(&self.0))
    }
}

#[must_use]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Diagnostic(imp::Diagnostic<db::FileId>);

impl fmt::Debug for Diagnostic {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // TODO
        write!(f, "Diagnostic({})", self.message())
    }
}

impl Diagnostic {
    pub fn bug(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::bug()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn error(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::error()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn warn(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::warning()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn info(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::note()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn help(message: impl Into<String>, primary_label: Label) -> Diagnostic {
        Diagnostic(
            imp::Diagnostic::help()
                .with_message(message)
                .with_labels(vec![primary_label.0]),
        )
    }

    pub fn emit(self, db: &'_ impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        let CtxInner { mode, counts, .. } = &mut *ctx.0.borrow_mut();
        match self.0.severity {
            imp::Severity::Bug => counts.bugs += 1,
            imp::Severity::Error => counts.errors += 1,
            imp::Severity::Warning => counts.warns += 1,
            imp::Severity::Note => (),
            imp::Severity::Help => counts.helps += 1,
        }
        match mode {
            Mode::Term { writer } => {
                term::emit(writer, &Default::default(), &db::FilesWrapper(db), &self.0)
                    .expect("failed to emit diagnostic")
            }
            Mode::Test { errs } => errs.push(self.0.message),
        }
    }

    pub fn with_note(mut self, note: impl Into<String>) -> Self {
        self.0.notes.push(note.into());
        self
    }

    pub fn with_notes(mut self, notes: impl IntoIterator<Item = String>) -> Diagnostic {
        self.0.notes.extend(notes);
        self
    }

    pub fn with_label(mut self, secondary_label: Label) -> Diagnostic {
        self.0.labels.push(secondary_label.0);
        self
    }

    pub fn with_labels(mut self, seconadry_labels: impl IntoIterator<Item = Label>) -> Diagnostic {
        self.0
            .labels
            .extend(seconadry_labels.into_iter().map(|l| l.0));
        self
    }

    pub fn message(&self) -> &str {
        &self.0.message
    }
}

/// An ordered list of diagnostics.
// TODO: panic if a Diagnostic[s] is dropped without ever being emitted
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[must_use]
pub struct Diagnostics {
    val: Vec<Diagnostic>,
}
impl Diagnostics {
    pub fn new() -> Diagnostics {
        Diagnostics { val: vec![] }
    }

    pub fn build(f: impl FnOnce(&mut Diagnostics)) -> Diagnostics {
        let mut diags = Diagnostics::new();
        f(&mut diags);
        diags
    }

    /// Adds a diagnostic.
    pub fn add(&mut self, diag: Diagnostic) {
        self.val.push(diag);
    }

    /// Combines diagnostics from `self` and `second` in order.
    ///
    /// This is useful when combining diagnostics from a query result with those
    /// from another operation, for example.
    ///
    /// The conventional way to call this method is using UFCS, e.g.
    ///
    /// ```
    /// Diagnostics::merge(&first, &second)
    /// ```
    ///
    /// but it can be useful to call as a method when chaining multiple merges.
    pub fn merge(&self, second: &Diagnostics) -> Diagnostics {
        // One day we can optimize the data structure for this case, instead of
        // cloning. We'll probably use Arcs, but optimize for the empty case.
        Diagnostics {
            val: self.val.iter().chain(second.val.iter()).cloned().collect(),
        }
    }

    /// Consumes `other`, adding all diagnostics to `self`.
    pub fn append(&mut self, mut other: Diagnostics) {
        self.val.append(&mut other.val);
    }

    pub fn emit(self, db: &impl db::BasicFileCache, ctx: &DiagnosticsCtx) {
        for diag in self.val {
            diag.emit(db, ctx);
        }
    }

    pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
        self.val.iter()
    }
}
impl From<Diagnostic> for Diagnostics {
    fn from(err: Diagnostic) -> Self {
        Diagnostics { val: vec![err] }
    }
}
impl From<Diagnostics> for Vec<Diagnostic> {
    fn from(diags: Diagnostics) -> Self {
        diags.val
    }
}

/// A value, plus any diagnostics that occurred while computing the value.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[must_use]
pub struct Outcome<T> {
    val: T,
    err: Diagnostics,
}
impl<T> Outcome<T> {
    pub fn from_ok(val: T) -> Outcome<T> {
        Outcome {
            val,
            err: Diagnostics::new(),
        }
    }

    pub fn from_err(val: T, err: Diagnostic) -> Outcome<T> {
        Outcome {
            val,
            err: err.into(),
        }
    }

    pub fn is_ok(&self) -> bool {
        self.err.val.is_empty()
    }

    pub fn as_ref<'a>(&'a self) -> RefOutcome<'a, T> {
        RefOutcome {
            val: &self.val,
            err: &self.err,
        }
    }

    // Use this when you need to bypass Arc::as_ref.
    pub fn to_ref<'a>(&'a self) -> RefOutcome<'a, T> {
        self.as_ref()
    }

    pub fn val(self) -> Result<T, Diagnostics> {
        if self.is_ok() {
            Ok(self.val)
        } else {
            Err(self.err)
        }
    }

    pub fn skip_errs(self) -> T {
        self.val
    }

    pub fn errs(self) -> Diagnostics {
        self.err
    }

    pub fn split(self) -> (T, Diagnostics) {
        (self.val, self.err)
    }

    pub fn then<R>(self, f: impl FnOnce(T) -> Outcome<R>) -> Outcome<R> {
        let outcome = f(self.val);
        Outcome {
            val: outcome.val,
            err: Diagnostics::merge(&self.err, &outcome.err),
        }
    }

    pub fn map<R>(self, f: impl FnOnce(T) -> R) -> Outcome<R> {
        Outcome {
            val: f(self.val),
            err: self.err,
        }
    }
}

pub fn ok<T>(val: T) -> Outcome<T> {
    Outcome::from_ok(val)
}

pub fn err<T>(val: T, err: Diagnostic) -> Outcome<T> {
    Outcome::from_err(val, err)
}

pub struct RefOutcome<'a, T> {
    val: &'a T,
    err: &'a Diagnostics,
}

impl<'a, T> RefOutcome<'a, T> {
    pub fn is_ok(&self) -> bool {
        self.err.val.is_empty()
    }

    pub fn val(self) -> Result<&'a T, &'a Diagnostics> {
        if self.is_ok() {
            Ok(self.val)
        } else {
            Err(self.err)
        }
    }

    pub fn skip_errs(self) -> &'a T {
        self.val
    }

    pub fn errs(self) -> &'a Diagnostics {
        self.err
    }

    pub fn split(self) -> (&'a T, &'a Diagnostics) {
        (self.val, self.err)
    }

    pub fn then<R>(self, f: impl FnOnce(&'a T) -> Outcome<R>) -> Outcome<R> {
        let outcome = f(self.val);
        Outcome {
            val: outcome.val,
            err: Diagnostics::merge(&self.err, &outcome.err),
        }
    }
}

impl<A, B> FromIterator<Outcome<A>> for Outcome<B>
where
    B: FromIterator<A>,
{
    fn from_iter<I>(iter: I) -> Self
    where
        I: IntoIterator<Item = Outcome<A>>,
    {
        let mut errs = Diagnostics::new();
        let vals = iter.into_iter().map(|oc| {
            errs.append(oc.err);
            oc.val
        });
        Outcome {
            val: B::from_iter(vals),
            err: errs,
        }
    }
}

impl<T> From<(T, Diagnostics)> for Outcome<T> {
    fn from(x: (T, Diagnostics)) -> Self {
        Outcome { val: x.0, err: x.1 }
    }
}

impl From<Diagnostics> for Outcome<()> {
    fn from(err: Diagnostics) -> Self {
        Outcome { val: (), err }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![feature(generic_associated_types)]
#![allow(dead_code)]

pub mod datatype;
pub mod fixpoint_iter;
pub mod graph;
pub mod wpo;
<commit_msg>update to Rust 1.65.0<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![allow(dead_code)]

pub mod datatype;
pub mod fixpoint_iter;
pub mod graph;
pub mod wpo;
<|endoftext|>


<commit_before>//! Definition of Thinkerbell scripts.
//!
//! Typical applications will not interact with script
//! objects. Rather, they will use module `parse` to parse a script
//! and module `run` to execute it.

use foxbox_taxonomy::selector::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::*;

use std::marker::PhantomData;

/// A thinkerbell script.
#[derive(Debug)]
pub struct Script<Ctx> where Ctx: Context {
    pub name: String,

    /// A set of rules, stating what must be done in which circumstance.
    pub rules: Vec<Rule<Ctx>>,

    pub phantom: PhantomData<Ctx>,
}

impl Parser<Script<UncheckedCtx>> for Script<UncheckedCtx> {
    fn description() -> String {
        "Script".to_owned()
    }
    fn parse(path: Path, source: &mut JSON) -> Result<Self, ParseError> {
        let name =  try!(path.push("", |path| String::take(path, source, "name")));
        let rules = try!(path.push("", |path| Rule::take_vec(path, source, "rules")));
        Ok(Script {
            name: name,
            rules: rules,
            phantom: PhantomData
        })
    }
}

/// A single rule, i.e. "when some condition becomes true, do
/// something".
///
/// # JSON
///
/// A single rule is represented as an object with the following fields:
///
/// - conditions (array of Match): the conditions in which to execute
///   the code – *all* conditions must be met;
/// - execute (array of Statement): the code to execute once all conditions
///   are met.
///
/// ```
/// extern crate foxbox_thinkerbell;
/// extern crate foxbox_taxonomy;
///
/// use foxbox_thinkerbell::ast::*;
/// use foxbox_taxonomy::parse::*;
///
/// # fn main() {
/// let source = r#"{
///   "conditions": [{
///     "source": [{"id": "my getter"}],
///     "kind": "OvenTemperature",
///     "range": {"Geq": {"Temperature": {"C": 300}}},
///     "duration": 3600
///   }],
///   "execute": [{
///     "destination": [{"id": "my setter"}],
///     "value": {"OnOff": "Off"},
///     "kind": "OnOff"
///   }]
/// }"#;
///
/// Rule::<UncheckedCtx>::from_str(&source).unwrap();
/// # }
/// ```
#[derive(Debug)]
pub struct Rule<Ctx> where Ctx: Context {
    /// The condition in which to execute the trigger. The condition
    /// is matched once *all* the `Match` branches are true. Whenever
    /// `conditions` was false and becomes true, we execute `execute`.
    pub conditions: Vec<Match<Ctx>>,

    /// Stuff to do once `condition` is met.
    pub execute: Vec<Statement<Ctx>>,

    pub phantom: PhantomData<Ctx>,
}
impl Parser<Rule<UncheckedCtx>> for Rule<UncheckedCtx> {
    fn description() -> String {
        "Rule".to_owned()
    }

    fn parse(path: Path, source: &mut JSON) -> Result<Self, ParseError> {
        let conditions = try!(path.push("conditions",
            |path| Match::take_vec(path, source, "conditions"))
        );
        let execute = try!(path.push("execute",
            |path| Statement::take_vec(path, source, "execute"))
        );
        Ok(Rule {
            conditions: conditions,
            execute: execute,
            phantom: PhantomData,
        })
    }
}

/// An individual match.
///
/// Matchs always take the form: "data received from getter channel
/// enters given range".
///
/// A condition is true if *any* of the corresponding getter channels
/// yielded a value that enters the given range.
///
/// # JSON
///
/// A match is represented as an object with the following fields:
///
/// - source (array of GetterSelector) - the selector for getters that will
///   provide the data;
/// - kind (ChannelKind) - the kind of getters;
/// - range (Range) - the condition in whih the match is considered met –
///   a match becomes met when any of the sources *enters* the range;
/// - duration (Duration, optional) - if provided, the match is only considered
///   met if any of the sources *enters* and *remains* in the range
///   for `duration`
///
/// ```
/// extern crate foxbox_thinkerbell;
/// extern crate foxbox_taxonomy;
///
/// use foxbox_thinkerbell::ast::*;
/// use foxbox_taxonomy::services::*;
/// use foxbox_taxonomy::parse::*;
///
/// # fn main() {
/// let source = r#"{
///   "source": [{"id": "my getter"}],
///   "kind": "OvenTemperature",
///   "range": {"Geq": {"Temperature": {"C": 300}}},
///   "duration": 3600
/// }"#;
///
/// let match_ = Match::<UncheckedCtx>::from_str(&source).unwrap();
/// assert_eq!(match_.kind, ChannelKind::OvenTemperature);
/// # }
/// ```
#[derive(Debug)]
pub struct Match<Ctx> where Ctx: Context {
    /// The set of getters to watch. Note that the set of getters may
    /// change (e.g. when devices are added/removed) without rebooting
    /// the script.
    pub source: Vec<GetterSelector>,

    /// The kind of channel expected from `source`, e.g. "the current
    /// time of day", "is the door opened?", etc. During compilation,
    /// we make sure that we restrict to the elements of `source` that
    /// offer `kind`.
    pub kind: ChannelKind,

    /// The range of values for which the condition is considered met.
    /// During compilation, we check that the type of `range` is
    /// compatible with that of `getter`.
    pub range: Range,

    /// If specified, the values must remain in the `range` for at least
    /// `duration` before the match is considered valid. This is useful
    /// for sensors that may oscillate around a threshold or for detecting
    /// e.g. that a door has been forgotten open.
    pub duration: Option<Duration>,

    pub phantom: PhantomData<Ctx>,
}
impl Parser<Match<UncheckedCtx>> for Match<UncheckedCtx> {
    fn description() -> String {
        "Match".to_owned()
    }

    fn parse(path: Path, source: &mut JSON) -> Result<Self, ParseError> {
        let sources = try!(path.push("source",
            |path| GetterSelector::take_vec(path, source, "source"))
        );
        let kind = try!(path.push("kind",
            |path| ChannelKind::take(path, source, "kind"))
        );
        let range = try!(path.push("range",
            |path| Range::take(path, source, "range"))
        );
        let duration = match path.push("range",
            |path| Duration::take(path, source, "duration"))
        {
            Err(ParseError::MissingField {..}) => None,
            Err(err) => return Err(err),
            Ok(ok) => Some(ok)
        };
        Ok(Match {
            source: sources,
            kind: kind,
            range: range,
            duration: duration,
            phantom: PhantomData,
        })
    }
}

/// Stuff to actually do. In practice, this means placing calls to devices.
///
/// # JSON
///
/// A statement is represented as an object with the following fields:
/// - destination (array of SetterSelector);
/// - value (Value);
/// - kind (ChannelKind);
///
/// ```
/// extern crate foxbox_thinkerbell;
/// extern crate foxbox_taxonomy;
///
/// use foxbox_thinkerbell::ast::*;
/// use foxbox_taxonomy::values::*;
/// use foxbox_taxonomy::services::*;
/// use foxbox_taxonomy::parse::*;
///
/// # fn main() {
/// let source = r#"{
///   "destination": [{"id": "my setter"}],
///   "value": {"OnOff": "Off"},
///   "kind": "OnOff"
/// }"#;
///
/// let statement = Statement::<UncheckedCtx>::from_str(&source).unwrap();
/// assert_eq!(statement.value, Value::OnOff(OnOff::Off));
/// assert_eq!(statement.kind, ChannelKind::OnOff);
/// # }
/// ```
#[derive(Debug)]
pub struct Statement<Ctx> where Ctx: Context {
    /// The set of setters to which to send a command. Note that the
    /// set of setters may change (e.g. when devices are
    /// added/removed) without rebooting the script.
    pub destination: Vec<SetterSelector>,

    /// Data to send to the resource. During compilation, we check
    /// that the type of `value` is compatible with that of
    /// `destination`.
    pub value: Value,

    /// The kind of channel expected from `destination`, e.g. "close
    /// the door", "set the temperature", etc. During compilation, we
    /// make sure that we restrict to the elements of `destination` that
    /// offer `kind`.
    pub kind: ChannelKind,

    pub phantom: PhantomData<Ctx>,
}
impl Parser<Statement<UncheckedCtx>> for Statement<UncheckedCtx> {
    fn description() -> String {
        "Parser".to_owned()
    }

    fn parse(path: Path, source: &mut JSON) -> Result<Self, ParseError> {
        let destination = try!(path.push("destination",
            |path| SetterSelector::take_vec(path, source, "destination"))
        );
        let kind = try!(path.push("kind",
            |path| ChannelKind::take(path, source, "kind"))
        );
        let value = try!(path.push("value",
            |path| Value::take(path, source, "value"))
        );
        Ok(Statement {
            destination: destination,
            value: value,
            kind: kind,
            phantom: PhantomData,
        })
    }
}

/// A manner of representing internal nodes.
///
/// Two data structures implement `Context`:
///
/// - `UncheckedCtx`, designed to mark the fact that a script has not
/// been compiled/checked yet and must not be executed;
/// - `compile::CompiledCtx`, designed to mark the fact that a script
/// has been compiled and can be executed.
pub trait Context {
}

/// A Context used to represent a script that hasn't been compiled
/// yet.
#[derive(Debug)]
pub struct UncheckedCtx;
impl Context for UncheckedCtx {
}

<commit_msg>Fix tests. r=Yoric<commit_after>//! Definition of Thinkerbell scripts.
//!
//! Typical applications will not interact with script
//! objects. Rather, they will use module `parse` to parse a script
//! and module `run` to execute it.

use foxbox_taxonomy::selector::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::*;

use std::marker::PhantomData;

/// A thinkerbell script.
#[derive(Debug)]
pub struct Script<Ctx> where Ctx: Context {
    pub name: String,

    /// A set of rules, stating what must be done in which circumstance.
    pub rules: Vec<Rule<Ctx>>,

    pub phantom: PhantomData<Ctx>,
}

impl Parser<Script<UncheckedCtx>> for Script<UncheckedCtx> {
    fn description() -> String {
        "Script".to_owned()
    }
    fn parse(path: Path, source: &mut JSON) -> Result<Self, ParseError> {
        let name =  try!(path.push("", |path| String::take(path, source, "name")));
        let rules = try!(path.push("", |path| Rule::take_vec(path, source, "rules")));
        Ok(Script {
            name: name,
            rules: rules,
            phantom: PhantomData
        })
    }
}

/// A single rule, i.e. "when some condition becomes true, do
/// something".
///
/// # JSON
///
/// A single rule is represented as an object with the following fields:
///
/// - conditions (array of Match): the conditions in which to execute
///   the code – *all* conditions must be met;
/// - execute (array of Statement): the code to execute once all conditions
///   are met.
///
/// ```
/// extern crate foxbox_thinkerbell;
/// extern crate foxbox_taxonomy;
///
/// use foxbox_thinkerbell::ast::*;
/// use foxbox_taxonomy::parse::*;
///
/// # fn main() {
/// let source = r#"{
///   "conditions": [{
///     "source": [{"id": "my getter"}],
///     "kind": "OvenTemperature",
///     "range": {"Geq": {"Temperature": {"C": 300}}},
///     "duration": 3600
///   }],
///   "execute": [{
///     "destination": [{"id": "my setter"}],
///     "value": {"OnOff": "Off"},
///     "kind": "LightOn"
///   }]
/// }"#;
///
/// Rule::<UncheckedCtx>::from_str(&source).unwrap();
/// # }
/// ```
#[derive(Debug)]
pub struct Rule<Ctx> where Ctx: Context {
    /// The condition in which to execute the trigger. The condition
    /// is matched once *all* the `Match` branches are true. Whenever
    /// `conditions` was false and becomes true, we execute `execute`.
    pub conditions: Vec<Match<Ctx>>,

    /// Stuff to do once `condition` is met.
    pub execute: Vec<Statement<Ctx>>,

    pub phantom: PhantomData<Ctx>,
}
impl Parser<Rule<UncheckedCtx>> for Rule<UncheckedCtx> {
    fn description() -> String {
        "Rule".to_owned()
    }

    fn parse(path: Path, source: &mut JSON) -> Result<Self, ParseError> {
        let conditions = try!(path.push("conditions",
            |path| Match::take_vec(path, source, "conditions"))
        );
        let execute = try!(path.push("execute",
            |path| Statement::take_vec(path, source, "execute"))
        );
        Ok(Rule {
            conditions: conditions,
            execute: execute,
            phantom: PhantomData,
        })
    }
}

/// An individual match.
///
/// Matchs always take the form: "data received from getter channel
/// enters given range".
///
/// A condition is true if *any* of the corresponding getter channels
/// yielded a value that enters the given range.
///
/// # JSON
///
/// A match is represented as an object with the following fields:
///
/// - source (array of GetterSelector) - the selector for getters that will
///   provide the data;
/// - kind (ChannelKind) - the kind of getters;
/// - range (Range) - the condition in whih the match is considered met –
///   a match becomes met when any of the sources *enters* the range;
/// - duration (Duration, optional) - if provided, the match is only considered
///   met if any of the sources *enters* and *remains* in the range
///   for `duration`
///
/// ```
/// extern crate foxbox_thinkerbell;
/// extern crate foxbox_taxonomy;
///
/// use foxbox_thinkerbell::ast::*;
/// use foxbox_taxonomy::services::*;
/// use foxbox_taxonomy::parse::*;
///
/// # fn main() {
/// let source = r#"{
///   "source": [{"id": "my getter"}],
///   "kind": "OvenTemperature",
///   "range": {"Geq": {"Temperature": {"C": 300}}},
///   "duration": 3600
/// }"#;
///
/// let match_ = Match::<UncheckedCtx>::from_str(&source).unwrap();
/// assert_eq!(match_.kind, ChannelKind::OvenTemperature);
/// # }
/// ```
#[derive(Debug)]
pub struct Match<Ctx> where Ctx: Context {
    /// The set of getters to watch. Note that the set of getters may
    /// change (e.g. when devices are added/removed) without rebooting
    /// the script.
    pub source: Vec<GetterSelector>,

    /// The kind of channel expected from `source`, e.g. "the current
    /// time of day", "is the door opened?", etc. During compilation,
    /// we make sure that we restrict to the elements of `source` that
    /// offer `kind`.
    pub kind: ChannelKind,

    /// The range of values for which the condition is considered met.
    /// During compilation, we check that the type of `range` is
    /// compatible with that of `getter`.
    pub range: Range,

    /// If specified, the values must remain in the `range` for at least
    /// `duration` before the match is considered valid. This is useful
    /// for sensors that may oscillate around a threshold or for detecting
    /// e.g. that a door has been forgotten open.
    pub duration: Option<Duration>,

    pub phantom: PhantomData<Ctx>,
}
impl Parser<Match<UncheckedCtx>> for Match<UncheckedCtx> {
    fn description() -> String {
        "Match".to_owned()
    }

    fn parse(path: Path, source: &mut JSON) -> Result<Self, ParseError> {
        let sources = try!(path.push("source",
            |path| GetterSelector::take_vec(path, source, "source"))
        );
        let kind = try!(path.push("kind",
            |path| ChannelKind::take(path, source, "kind"))
        );
        let range = try!(path.push("range",
            |path| Range::take(path, source, "range"))
        );
        let duration = match path.push("range",
            |path| Duration::take(path, source, "duration"))
        {
            Err(ParseError::MissingField {..}) => None,
            Err(err) => return Err(err),
            Ok(ok) => Some(ok)
        };
        Ok(Match {
            source: sources,
            kind: kind,
            range: range,
            duration: duration,
            phantom: PhantomData,
        })
    }
}

/// Stuff to actually do. In practice, this means placing calls to devices.
///
/// # JSON
///
/// A statement is represented as an object with the following fields:
/// - destination (array of SetterSelector);
/// - value (Value);
/// - kind (ChannelKind);
///
/// ```
/// extern crate foxbox_thinkerbell;
/// extern crate foxbox_taxonomy;
///
/// use foxbox_thinkerbell::ast::*;
/// use foxbox_taxonomy::values::*;
/// use foxbox_taxonomy::services::*;
/// use foxbox_taxonomy::parse::*;
///
/// # fn main() {
/// let source = r#"{
///   "destination": [{"id": "my setter"}],
///   "value": {"OnOff": "Off"},
///   "kind": "LightOn"
/// }"#;
///
/// let statement = Statement::<UncheckedCtx>::from_str(&source).unwrap();
/// assert_eq!(statement.value, Value::OnOff(OnOff::Off));
/// assert_eq!(statement.kind, ChannelKind::LightOn);
/// # }
/// ```
#[derive(Debug)]
pub struct Statement<Ctx> where Ctx: Context {
    /// The set of setters to which to send a command. Note that the
    /// set of setters may change (e.g. when devices are
    /// added/removed) without rebooting the script.
    pub destination: Vec<SetterSelector>,

    /// Data to send to the resource. During compilation, we check
    /// that the type of `value` is compatible with that of
    /// `destination`.
    pub value: Value,

    /// The kind of channel expected from `destination`, e.g. "close
    /// the door", "set the temperature", etc. During compilation, we
    /// make sure that we restrict to the elements of `destination` that
    /// offer `kind`.
    pub kind: ChannelKind,

    pub phantom: PhantomData<Ctx>,
}
impl Parser<Statement<UncheckedCtx>> for Statement<UncheckedCtx> {
    fn description() -> String {
        "Parser".to_owned()
    }

    fn parse(path: Path, source: &mut JSON) -> Result<Self, ParseError> {
        let destination = try!(path.push("destination",
            |path| SetterSelector::take_vec(path, source, "destination"))
        );
        let kind = try!(path.push("kind",
            |path| ChannelKind::take(path, source, "kind"))
        );
        let value = try!(path.push("value",
            |path| Value::take(path, source, "value"))
        );
        Ok(Statement {
            destination: destination,
            value: value,
            kind: kind,
            phantom: PhantomData,
        })
    }
}

/// A manner of representing internal nodes.
///
/// Two data structures implement `Context`:
///
/// - `UncheckedCtx`, designed to mark the fact that a script has not
/// been compiled/checked yet and must not be executed;
/// - `compile::CompiledCtx`, designed to mark the fact that a script
/// has been compiled and can be executed.
pub trait Context {
}

/// A Context used to represent a script that hasn't been compiled
/// yet.
#[derive(Debug)]
pub struct UncheckedCtx;
impl Context for UncheckedCtx {
}

<|endoftext|>


<commit_before>#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate radeco_lib;
extern crate r2pipe;
extern crate r2api;
extern crate base64;
extern crate rustyline;

use radeco_lib::analysis::cse::cse::CSE;
use radeco_lib::analysis::interproc::fixcall::CallFixer;
use radeco_lib::analysis::sccp;
use radeco_lib::backend::lang_c::c_cfg::CCFGVerifier;
use radeco_lib::backend::lang_c::c_cfg_builder;
use radeco_lib::frontend::radeco_containers::*;
use radeco_lib::middle::{dce, dot};
use radeco_lib::middle::ir_reader::parse_il;
use radeco_lib::middle::ir_writer;
use radeco_lib::middle::regfile::SubRegisterFile;
use radeco_lib::middle::ssa::ssastorage::SSAStorage;
use radeco_lib::middle::ssa::verifier;
use rustyline::Editor;

use rustyline::error::ReadlineError;

use std::collections::HashMap;
use std::process;
use std::str;
use std::sync::Arc;

// On unix platforms you can use ANSI escape sequences
#[cfg(unix)]
const PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";

// Windows consoles typically don't support ANSI escape sequences out
// of the box
#[cfg(windows)]
const PROMPT: &'static str = ">> ";

fn main() {
    let mut rl = Editor::<()>::new();
    let mut proj = None;
    loop {
        let readline = rl.readline(PROMPT);
        match readline {
            Ok(line) => {
                let mut terms = line.split_whitespace();
                let o1 = terms.next();
                let o2 = terms.next();
                cmd(o1, o2, &mut proj);
            }
            Err(ReadlineError::Interrupted) |
            Err(ReadlineError::Eof) => break,
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

mod command {
    pub const LOAD: &'static str = "load";
    pub const FNLIST: &'static str = "fn_list";
    pub const ANALYZE: &'static str = "analyze";
    pub const DOT: &'static str = "dot";
    pub const IR: &'static str = "ir";
    pub const DECOMPILE: &'static str = "decompile";
}

fn cmd(op1: Option<&str>, op2: Option<&str>, proj_opt: &mut Option<RadecoProject>) {
    match (op1, op2) {
        (Some(command::LOAD), Some(s)) => {
            let mut p = ProjectLoader::new().path(s).load();
            let regfile = p.regfile().clone();
            for mut xy in p.iter_mut() {
                analyze_mod(regfile.clone(), xy.module);
            }
            *proj_opt = Some(p);
            return;
        }
        _ => {}
    };
    if proj_opt.is_none() {
        println!("Load a project first");
        return;
    }
    let mut proj = proj_opt.as_mut().unwrap();
    match (op1, op2) {
        (Some(command::ANALYZE), Some("*")) => {
            let rfns = proj.iter_mut().map(|i| i.module).flat_map(|rmod| {
                rmod.functions.values_mut()
            });
            for rfn in rfns {
                analyze(rfn);
            }
        }
        (Some(command::FNLIST), _) => {
            let funcs = fn_list(&proj);
            println!("{}", funcs.join("\n"));
        }
        (Some(command::ANALYZE), Some(f)) => {
            if let Some(rfn) = get_function_mut(f, &mut proj) {
                analyze(rfn);
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DOT), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_dot(rfn.ssa()));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::IR), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_ir(rfn));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DECOMPILE), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                let rmod = proj.iter().map(|i| i.module).next().unwrap();
                let func_name_map = rmod.functions
                    .iter()
                    .map(|(&addr, f)| (addr, f.name.to_string()))
                    .collect();
                let strings = rmod.strings()
                    .iter()
                    .filter(|ref s| s.vaddr.is_some() && s.string.is_some())
                    .cloned()
                    .map(|s| {
                        let (addr, _s) = (s.vaddr.unwrap(), s.string.unwrap());
                        let bytes = base64::decode(&_s).unwrap_or(Vec::new());
                        let ret_string = match str::from_utf8(bytes.as_slice()) {
                            Ok(v) => v.to_string(),
                            Err(_e) => _s,
                        };
                        (addr, ret_string)
                    })
                    .collect();
                println!("{}", decompile(rfn, &func_name_map, &strings));
            } else {
                println!("{} is not found", f);
            }
        }
        _ => {}
    }
}

fn fn_list(proj: &RadecoProject) -> Vec<String> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .map(|s| s.name.to_string())
        .collect()
}

fn get_function<'a>(name: &str, proj: &'a RadecoProject) -> Option<&'a RadecoFunction> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn get_function_mut<'a>(name: &str, proj: &'a mut RadecoProject) -> Option<&'a mut RadecoFunction> {
    proj.iter_mut()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values_mut())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn analyze_mod(regfile: Arc<SubRegisterFile>, rmod: &mut RadecoModule) {
    // Analyze preserved for all functions.
    {
        println!("[*] Fixing Callee Information");
        let bp_name = regfile.get_name_by_alias(&"BP".to_string());
        let bp_name = bp_name.map(|s| s.to_owned());
        let sp_name = regfile.get_name_by_alias(&"SP".to_string());
        let sp_name = sp_name.map(|s| s.to_owned());
        let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
        callfixer.rounded_analysis();
    }

    // Fix call sites
    radeco_lib::analysis::functions::fix_ssa_opcalls::go(rmod);

    // Infer calling conventions
    radeco_lib::analysis::functions::infer_regusage::run(rmod, &*regfile);
}

fn analyze(rfn: &mut RadecoFunction) {
    println!("[+] Analyzing: {} @ {:#x}", rfn.name, rfn.offset);
    {
        println!("  [*] Eliminating Dead Code");
        dce::collect(rfn.ssa_mut());
    }
    let mut ssa = {
        // Constant Propagation (sccp)
        println!("  [*] Propagating Constants");
        let mut analyzer = sccp::Analyzer::new(rfn.ssa_mut());
        analyzer.analyze();
        analyzer.emit_ssa()
    };
    {
        println!("  [*] Eliminating More DeadCode");
        dce::collect(&mut ssa);
    }
    *rfn.ssa_mut() = ssa;
    {
        // Common SubExpression Elimination (cse)
        println!("  [*] Eliminating Common SubExpressions");
        let mut cse = CSE::new(rfn.ssa_mut());
        cse.run();
    }
    {
        // Verify SSA
        println!("  [*] Verifying SSA's Validity");
        match verifier::verify(rfn.ssa()) {
            Err(e) => {
                println!("  [*] Found Error: {}", e);
                process::exit(255);
            }
            Ok(_) => {}
        }
    }
}

fn emit_ir(rfn: &RadecoFunction) -> String {
    println!("  [*] Writing out IR");
    let mut res = String::new();
    ir_writer::emit_il(&mut res, Some(rfn.name.to_string()), rfn.ssa()).unwrap();
    res
}

fn parse_ir(name: &str, ir_str: &str, regfile: Arc<SubRegisterFile>) {
    println!("  [*] Testing IR parser");
    let parsed = parse_il(ir_str, regfile.clone());
    let mut res = String::new();
    ir_writer::emit_il(&mut res, Some(name.to_string()), &parsed).unwrap();
    for (orig, roundtrip) in ir_str.lines().zip(res.lines()) {
        if orig != roundtrip {
            println!("  FAILED TO ROUND-TRIP: \"{}\" => \"{}\"", orig, roundtrip);
        }
    }
}

fn emit_dot(ssa: &SSAStorage) -> String {
    dot::emit_dot(ssa)
}

fn decompile(
    rfn: &RadecoFunction,
    func_name_map: &HashMap<u64, String>,
    strings: &HashMap<u64, String>,
) -> String {
    let c_cfg = c_cfg_builder::recover_c_cfg(rfn, func_name_map, strings);
    if let Err(err) = CCFGVerifier::verify(&c_cfg) {
        println!("CCFG verification failed");
        println!("{}", err);
    }
    c_cfg.to_c_ast().print()
}
<commit_msg>Minor fixes<commit_after>#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate radeco_lib;
extern crate r2pipe;
extern crate r2api;
extern crate base64;
extern crate rustyline;

use radeco_lib::analysis::cse::cse::CSE;
use radeco_lib::analysis::interproc::fixcall::CallFixer;
use radeco_lib::analysis::sccp;
use radeco_lib::backend::lang_c::c_cfg::CCFGVerifier;
use radeco_lib::backend::lang_c::c_cfg_builder;
use radeco_lib::frontend::radeco_containers::*;
use radeco_lib::middle::{dce, dot};
use radeco_lib::middle::ir_writer;
use radeco_lib::middle::regfile::SubRegisterFile;
use radeco_lib::middle::ssa::ssastorage::SSAStorage;
use radeco_lib::middle::ssa::verifier;
use rustyline::Editor;

use rustyline::error::ReadlineError;

use std::collections::HashMap;
use std::process;
use std::str;
use std::sync::Arc;

// On unix platforms you can use ANSI escape sequences
#[cfg(unix)]
const PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";

// Windows consoles typically don't support ANSI escape sequences out
// of the box
#[cfg(windows)]
const PROMPT: &'static str = ">> ";

fn main() {
    let mut rl = Editor::<()>::new();
    let mut proj = None;
    loop {
        let readline = rl.readline(PROMPT);
        match readline {
            Ok(line) => {
                let mut terms = line.split_whitespace();
                let o1 = terms.next();
                let o2 = terms.next();
                cmd(o1, o2, &mut proj);
            }
            Err(ReadlineError::Interrupted) |
            Err(ReadlineError::Eof) => break,
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

mod command {
    pub const LOAD: &'static str = "load";
    pub const FNLIST: &'static str = "fn_list";
    pub const ANALYZE: &'static str = "analyze";
    pub const DOT: &'static str = "dot";
    pub const IR: &'static str = "ir";
    pub const DECOMPILE: &'static str = "decompile";
}

fn cmd(op1: Option<&str>, op2: Option<&str>, proj_opt: &mut Option<RadecoProject>) {
    match (op1, op2) {
        (Some(command::LOAD), Some(s)) => {
            let mut p = ProjectLoader::new().path(s).load();
            let regfile = p.regfile().clone();
            for mut xy in p.iter_mut() {
                analyze_mod(regfile.clone(), xy.module);
            }
            *proj_opt = Some(p);
            return;
        }
        _ => {}
    };
    let mut proj = match proj_opt {
        Some(proj) => proj,
        None => {
            println!("Load a project first");
            return;
        }
    };
    match (op1, op2) {
        (Some(command::ANALYZE), Some("*")) => {
            let rfns = proj.iter_mut().map(|i| i.module).flat_map(|rmod| {
                rmod.functions.values_mut()
            });
            for rfn in rfns {
                analyze(rfn);
            }
        }
        (Some(command::FNLIST), _) => {
            let funcs = fn_list(&proj);
            println!("{}", funcs.join("\n"));
        }
        (Some(command::ANALYZE), Some(f)) => {
            if let Some(rfn) = get_function_mut(f, &mut proj) {
                analyze(rfn);
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DOT), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_dot(rfn.ssa()));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::IR), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_ir(rfn));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DECOMPILE), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                let rmod = proj.iter().map(|i| i.module).next().unwrap();
                let func_name_map = rmod.functions
                    .iter()
                    .map(|(&addr, f)| (addr, f.name.to_string()))
                    .collect();
                let strings = rmod.strings()
                    .iter()
                    .filter(|ref s| s.vaddr.is_some() && s.string.is_some())
                    .cloned()
                    .map(|s| {
                        let (addr, _s) = (s.vaddr.unwrap(), s.string.unwrap());
                        let bytes = base64::decode(&_s).unwrap_or(Vec::new());
                        let ret_string = match str::from_utf8(bytes.as_slice()) {
                            Ok(v) => v.to_string(),
                            Err(_e) => _s,
                        };
                        (addr, ret_string)
                    })
                    .collect();
                println!("{}", decompile(rfn, &func_name_map, &strings));
            } else {
                println!("{} is not found", f);
            }
        }
        _ => {}
    }
}

fn fn_list(proj: &RadecoProject) -> Vec<String> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .map(|s| s.name.to_string())
        .collect()
}

fn get_function<'a>(name: &str, proj: &'a RadecoProject) -> Option<&'a RadecoFunction> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn get_function_mut<'a>(name: &str, proj: &'a mut RadecoProject) -> Option<&'a mut RadecoFunction> {
    proj.iter_mut()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values_mut())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn analyze_mod(regfile: Arc<SubRegisterFile>, rmod: &mut RadecoModule) {
    // Analyze preserved for all functions.
    {
        println!("[*] Fixing Callee Information");
        let bp_name = regfile.get_name_by_alias(&"BP".to_string());
        let bp_name = bp_name.map(|s| s.to_owned());
        let sp_name = regfile.get_name_by_alias(&"SP".to_string());
        let sp_name = sp_name.map(|s| s.to_owned());
        let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
        callfixer.rounded_analysis();
    }

    // Fix call sites
    radeco_lib::analysis::functions::fix_ssa_opcalls::go(rmod);

    // Infer calling conventions
    radeco_lib::analysis::functions::infer_regusage::run(rmod, &*regfile);
}

fn analyze(rfn: &mut RadecoFunction) {
    println!("[+] Analyzing: {} @ {:#x}", rfn.name, rfn.offset);
    {
        println!("  [*] Eliminating Dead Code");
        dce::collect(rfn.ssa_mut());
    }
    let mut ssa = {
        // Constant Propagation (sccp)
        println!("  [*] Propagating Constants");
        let mut analyzer = sccp::Analyzer::new(rfn.ssa_mut());
        analyzer.analyze();
        analyzer.emit_ssa()
    };
    {
        println!("  [*] Eliminating More DeadCode");
        dce::collect(&mut ssa);
    }
    *rfn.ssa_mut() = ssa;
    {
        // Common SubExpression Elimination (cse)
        println!("  [*] Eliminating Common SubExpressions");
        let mut cse = CSE::new(rfn.ssa_mut());
        cse.run();
    }
    {
        // Verify SSA
        println!("  [*] Verifying SSA's Validity");
        match verifier::verify(rfn.ssa()) {
            Err(e) => {
                println!("  [*] Found Error: {}", e);
                process::exit(255);
            }
            Ok(_) => {}
        }
    }
}

fn emit_ir(rfn: &RadecoFunction) -> String {
    println!("  [*] Writing out IR");
    let mut res = String::new();
    ir_writer::emit_il(&mut res, Some(rfn.name.to_string()), rfn.ssa()).unwrap();
    res
}

fn emit_dot(ssa: &SSAStorage) -> String {
    dot::emit_dot(ssa)
}

fn decompile(
    rfn: &RadecoFunction,
    func_name_map: &HashMap<u64, String>,
    strings: &HashMap<u64, String>,
) -> String {
    let c_cfg = c_cfg_builder::recover_c_cfg(rfn, func_name_map, strings);
    if let Err(err) = CCFGVerifier::verify(&c_cfg) {
        println!("CCFG verification failed");
        println!("{}", err);
    }
    c_cfg.to_c_ast().print()
}
<|endoftext|>